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 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 23*1353Sericheng * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * Data-Link Driver 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/types.h> 340Sstevel@tonic-gate #include <sys/debug.h> 350Sstevel@tonic-gate #include <sys/sysmacros.h> 360Sstevel@tonic-gate #include <sys/stream.h> 370Sstevel@tonic-gate #include <sys/ddi.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate #include <sys/strsun.h> 401184Skrgopi #include <sys/cpuvar.h> 410Sstevel@tonic-gate #include <sys/dlpi.h> 420Sstevel@tonic-gate #include <netinet/in.h> 430Sstevel@tonic-gate #include <sys/sdt.h> 440Sstevel@tonic-gate #include <sys/strsubr.h> 450Sstevel@tonic-gate #include <sys/vlan.h> 460Sstevel@tonic-gate #include <sys/mac.h> 470Sstevel@tonic-gate #include <sys/dls.h> 480Sstevel@tonic-gate #include <sys/dld.h> 490Sstevel@tonic-gate #include <sys/dld_impl.h> 501184Skrgopi #include <sys/dls_soft_ring.h> 510Sstevel@tonic-gate 520Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *); 530Sstevel@tonic-gate 540Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req, 550Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req, 560Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req, 570Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req, 580Sstevel@tonic-gate proto_notify_req, proto_unitdata_req, proto_passive_req; 590Sstevel@tonic-gate 60269Sericheng static void proto_poll_disable(dld_str_t *); 611184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *); 62269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *); 630Sstevel@tonic-gate 64*1353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req; 65*1353Sericheng 661184Skrgopi static void proto_soft_ring_disable(dld_str_t *); 671184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *); 681184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *); 691184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int); 701184Skrgopi 710Sstevel@tonic-gate #define DL_ACK_PENDING(state) \ 720Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \ 730Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \ 740Sstevel@tonic-gate (state) == DL_BIND_PENDING || \ 750Sstevel@tonic-gate (state) == DL_UNBIND_PENDING) 760Sstevel@tonic-gate 770Sstevel@tonic-gate /* 78269Sericheng * Process a DLPI protocol message. 79269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ, 80269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an 81269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t 82269Sericheng * as 'passive' and forbids it from being subsequently made 'active' 83269Sericheng * by the above primitives. 840Sstevel@tonic-gate */ 850Sstevel@tonic-gate void 860Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp) 870Sstevel@tonic-gate { 880Sstevel@tonic-gate union DL_primitives *udlp; 890Sstevel@tonic-gate t_uscalar_t prim; 900Sstevel@tonic-gate 910Sstevel@tonic-gate if (MBLKL(mp) < sizeof (t_uscalar_t)) { 920Sstevel@tonic-gate freemsg(mp); 930Sstevel@tonic-gate return; 940Sstevel@tonic-gate } 950Sstevel@tonic-gate 960Sstevel@tonic-gate udlp = (union DL_primitives *)mp->b_rptr; 970Sstevel@tonic-gate prim = udlp->dl_primitive; 980Sstevel@tonic-gate 99269Sericheng switch (prim) { 100269Sericheng case DL_INFO_REQ: 101269Sericheng (void) proto_info_req(dsp, udlp, mp); 102269Sericheng break; 103269Sericheng case DL_BIND_REQ: 104269Sericheng (void) proto_bind_req(dsp, udlp, mp); 105269Sericheng break; 106269Sericheng case DL_UNBIND_REQ: 107269Sericheng (void) proto_unbind_req(dsp, udlp, mp); 108269Sericheng break; 109269Sericheng case DL_UNITDATA_REQ: 110269Sericheng (void) proto_unitdata_req(dsp, udlp, mp); 111269Sericheng break; 112269Sericheng case DL_UDQOS_REQ: 113269Sericheng (void) proto_udqos_req(dsp, udlp, mp); 114269Sericheng break; 115269Sericheng case DL_ATTACH_REQ: 116269Sericheng (void) proto_attach_req(dsp, udlp, mp); 117269Sericheng break; 118269Sericheng case DL_DETACH_REQ: 119269Sericheng (void) proto_detach_req(dsp, udlp, mp); 120269Sericheng break; 121269Sericheng case DL_ENABMULTI_REQ: 122269Sericheng (void) proto_enabmulti_req(dsp, udlp, mp); 123269Sericheng break; 124269Sericheng case DL_DISABMULTI_REQ: 125269Sericheng (void) proto_disabmulti_req(dsp, udlp, mp); 126269Sericheng break; 127269Sericheng case DL_PROMISCON_REQ: 128269Sericheng (void) proto_promiscon_req(dsp, udlp, mp); 129269Sericheng break; 130269Sericheng case DL_PROMISCOFF_REQ: 131269Sericheng (void) proto_promiscoff_req(dsp, udlp, mp); 132269Sericheng break; 133269Sericheng case DL_PHYS_ADDR_REQ: 134269Sericheng (void) proto_physaddr_req(dsp, udlp, mp); 135269Sericheng break; 136269Sericheng case DL_SET_PHYS_ADDR_REQ: 137269Sericheng (void) proto_setphysaddr_req(dsp, udlp, mp); 138269Sericheng break; 139269Sericheng case DL_NOTIFY_REQ: 140269Sericheng (void) proto_notify_req(dsp, udlp, mp); 141269Sericheng break; 142269Sericheng case DL_CAPABILITY_REQ: 143269Sericheng (void) proto_capability_req(dsp, udlp, mp); 144269Sericheng break; 145269Sericheng case DL_PASSIVE_REQ: 146269Sericheng (void) proto_passive_req(dsp, udlp, mp); 147269Sericheng break; 148269Sericheng default: 149269Sericheng (void) proto_req(dsp, udlp, mp); 150269Sericheng break; 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate } 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate /* 155*1353Sericheng * Finish any pending operations. 156*1353Sericheng * Requests that need to be processed asynchronously will be handled 157*1353Sericheng * by a separate thread. After this function returns, other threads 158*1353Sericheng * will be allowed to enter dld; they will not be able to do anything 159*1353Sericheng * until ds_dlstate transitions to a non-pending state. 1600Sstevel@tonic-gate */ 161269Sericheng void 162269Sericheng dld_finish_pending_ops(dld_str_t *dsp) 1630Sstevel@tonic-gate { 164*1353Sericheng task_func_t *op = NULL; 165*1353Sericheng 166269Sericheng ASSERT(MUTEX_HELD(&dsp->ds_thr_lock)); 167269Sericheng ASSERT(dsp->ds_thr == 0); 1680Sstevel@tonic-gate 169*1353Sericheng op = dsp->ds_pending_op; 170*1353Sericheng dsp->ds_pending_op = NULL; 171*1353Sericheng mutex_exit(&dsp->ds_thr_lock); 172*1353Sericheng if (op != NULL) 173*1353Sericheng (void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP); 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate 176269Sericheng #define NEG(x) -(x) 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate typedef struct dl_info_ack_wrapper { 1790Sstevel@tonic-gate dl_info_ack_t dl_info; 1800Sstevel@tonic-gate uint8_t dl_addr[MAXADDRLEN + sizeof (uint16_t)]; 1810Sstevel@tonic-gate uint8_t dl_brdcst_addr[MAXADDRLEN]; 1820Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1; 1830Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1; 1840Sstevel@tonic-gate } dl_info_ack_wrapper_t; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* 187269Sericheng * DL_INFO_REQ 1880Sstevel@tonic-gate */ 189269Sericheng /*ARGSUSED*/ 190269Sericheng static boolean_t 191269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1920Sstevel@tonic-gate { 1930Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp; 1940Sstevel@tonic-gate dl_info_ack_t *dlp; 1950Sstevel@tonic-gate dl_qos_cl_sel1_t *selp; 1960Sstevel@tonic-gate dl_qos_cl_range1_t *rangep; 1970Sstevel@tonic-gate uint8_t *addr; 1980Sstevel@tonic-gate uint8_t *brdcst_addr; 1990Sstevel@tonic-gate uint_t addr_length; 2000Sstevel@tonic-gate uint_t sap_length; 201269Sericheng mac_info_t minfo; 202269Sericheng mac_info_t *minfop; 203269Sericheng queue_t *q = dsp->ds_wq; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate /* 2060Sstevel@tonic-gate * Swap the request message for one large enough to contain the 2070Sstevel@tonic-gate * wrapper structure defined above. 2080Sstevel@tonic-gate */ 209269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), 2100Sstevel@tonic-gate M_PCPROTO, 0)) == NULL) 211269Sericheng return (B_FALSE); 212269Sericheng 213269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); 2160Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate dlp = &(dlwp->dl_info); 2190Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK; 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate /* 2240Sstevel@tonic-gate * Set up the sub-structure pointers. 2250Sstevel@tonic-gate */ 2260Sstevel@tonic-gate addr = dlwp->dl_addr; 2270Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr; 2280Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1); 2290Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1); 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate /* 2320Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider 2330Sstevel@tonic-gate * nodes. 2340Sstevel@tonic-gate */ 2350Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS; 2360Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2; 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate /* 239269Sericheng * Set the style of the provider 2400Sstevel@tonic-gate */ 241269Sericheng dlp->dl_provider_style = dsp->ds_style; 2420Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 || 2430Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2); 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate /* 2460Sstevel@tonic-gate * Set the current DLPI state. 2470Sstevel@tonic-gate */ 2480Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate; 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate /* 251269Sericheng * Gratuitously set the media type. This is to deal with modules 252269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ 2530Sstevel@tonic-gate * being completed. 2540Sstevel@tonic-gate */ 2550Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER; 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate /* 258269Sericheng * If the stream is not at least attached we try to retrieve the 259269Sericheng * mac_info using mac_info_get() 2600Sstevel@tonic-gate */ 2610Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED || 2620Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING || 263269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) { 264269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { 265269Sericheng /* 266269Sericheng * Cannot find mac_info. giving up. 267269Sericheng */ 268269Sericheng goto done; 269269Sericheng } 270269Sericheng minfop = &minfo; 271269Sericheng } else { 272269Sericheng minfop = (mac_info_t *)dsp->ds_mip; 273269Sericheng } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * Set the media type (properly this time). 2770Sstevel@tonic-gate */ 278269Sericheng dlp->dl_mac_type = minfop->mi_media; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate /* 2810Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2820Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2830Sstevel@tonic-gate */ 2840Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2850Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate /* 2880Sstevel@tonic-gate * Set the minimum and maximum payload sizes. 2890Sstevel@tonic-gate */ 290269Sericheng dlp->dl_min_sdu = minfop->mi_sdu_min; 291269Sericheng dlp->dl_max_sdu = minfop->mi_sdu_max; 2920Sstevel@tonic-gate 293269Sericheng addr_length = minfop->mi_addr_length; 2940Sstevel@tonic-gate ASSERT(addr_length != 0); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate /* 2970Sstevel@tonic-gate * Copy in the media broadcast address. 2980Sstevel@tonic-gate */ 2990Sstevel@tonic-gate dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp; 300269Sericheng bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 3010Sstevel@tonic-gate dlp->dl_brdcst_addr_length = addr_length; 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* 3040Sstevel@tonic-gate * We only support QoS information for VLAN interfaces. 3050Sstevel@tonic-gate */ 3060Sstevel@tonic-gate if (dsp->ds_vid != VLAN_ID_NONE) { 3070Sstevel@tonic-gate dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 3080Sstevel@tonic-gate dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 3090Sstevel@tonic-gate 3100Sstevel@tonic-gate rangep->dl_qos_type = DL_QOS_CL_RANGE1; 3110Sstevel@tonic-gate rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 3120Sstevel@tonic-gate rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 3130Sstevel@tonic-gate rangep->dl_protection.dl_min = DL_UNKNOWN; 3140Sstevel@tonic-gate rangep->dl_protection.dl_max = DL_UNKNOWN; 3150Sstevel@tonic-gate rangep->dl_residual_error = DL_UNKNOWN; 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * Specify the supported range of priorities. 3190Sstevel@tonic-gate */ 3200Sstevel@tonic-gate rangep->dl_priority.dl_min = 0; 3210Sstevel@tonic-gate rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 3240Sstevel@tonic-gate dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate selp->dl_qos_type = DL_QOS_CL_SEL1; 3270Sstevel@tonic-gate selp->dl_trans_delay = DL_UNKNOWN; 3280Sstevel@tonic-gate selp->dl_protection = DL_UNKNOWN; 3290Sstevel@tonic-gate selp->dl_residual_error = DL_UNKNOWN; 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* 3320Sstevel@tonic-gate * Specify the current priority (which can be changed by 3330Sstevel@tonic-gate * the DL_UDQOS_REQ primitive). 3340Sstevel@tonic-gate */ 3350Sstevel@tonic-gate selp->dl_priority = dsp->ds_pri; 3360Sstevel@tonic-gate } else { 3370Sstevel@tonic-gate /* 3380Sstevel@tonic-gate * Shorten the buffer to lose the unused QoS information 339269Sericheng * structures. 3400Sstevel@tonic-gate */ 3410Sstevel@tonic-gate mp->b_wptr = (uint8_t *)rangep; 3420Sstevel@tonic-gate } 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 3450Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 3460Sstevel@tonic-gate /* 3470Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 3480Sstevel@tonic-gate * DLSAP address. 3490Sstevel@tonic-gate */ 3500Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 3510Sstevel@tonic-gate bcopy(dsp->ds_curr_addr, addr, addr_length); 3520Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3530Sstevel@tonic-gate } 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate done: 3560Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3570Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3580Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3590Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3600Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3610Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3620Sstevel@tonic-gate 363269Sericheng rw_exit(&dsp->ds_lock); 364269Sericheng 365269Sericheng qreply(q, mp); 366269Sericheng return (B_TRUE); 367269Sericheng } 368269Sericheng 369269Sericheng /* 370269Sericheng * DL_ATTACH_REQ 371269Sericheng */ 372269Sericheng static boolean_t 373269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 374269Sericheng { 375269Sericheng dl_attach_req_t *dlp = (dl_attach_req_t *)udlp; 376269Sericheng int err = 0; 377269Sericheng t_uscalar_t dl_err; 378269Sericheng queue_t *q = dsp->ds_wq; 379269Sericheng 380269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 381269Sericheng 382269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 383269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 384269Sericheng dl_err = DL_BADPRIM; 385269Sericheng goto failed; 386269Sericheng } 387269Sericheng 388269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 389269Sericheng dl_err = DL_OUTSTATE; 390269Sericheng goto failed; 391269Sericheng } 392269Sericheng 393269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 394269Sericheng 395269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 396269Sericheng if (err != 0) { 397269Sericheng switch (err) { 398269Sericheng case ENOENT: 399269Sericheng dl_err = DL_BADPPA; 400269Sericheng err = 0; 401269Sericheng break; 402269Sericheng default: 403269Sericheng dl_err = DL_SYSERR; 404269Sericheng break; 405269Sericheng } 406269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 407269Sericheng goto failed; 408269Sericheng } 409269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 410269Sericheng rw_exit(&dsp->ds_lock); 411269Sericheng 412269Sericheng dlokack(q, mp, DL_ATTACH_REQ); 413269Sericheng return (B_TRUE); 414269Sericheng failed: 415269Sericheng rw_exit(&dsp->ds_lock); 416269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 417269Sericheng return (B_FALSE); 4180Sstevel@tonic-gate } 4190Sstevel@tonic-gate 4200Sstevel@tonic-gate /* 421269Sericheng * DL_DETACH_REQ 4220Sstevel@tonic-gate */ 423*1353Sericheng static void 424*1353Sericheng proto_process_detach_req(void *arg) 425*1353Sericheng { 426*1353Sericheng dld_str_t *dsp = arg; 427*1353Sericheng mblk_t *mp; 428*1353Sericheng 429*1353Sericheng /* 430*1353Sericheng * We don't need to hold locks because no other thread 431*1353Sericheng * would manipulate dsp while it is in a PENDING state. 432*1353Sericheng */ 433*1353Sericheng ASSERT(dsp->ds_pending_req != NULL); 434*1353Sericheng ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING); 435*1353Sericheng 436*1353Sericheng mp = dsp->ds_pending_req; 437*1353Sericheng dsp->ds_pending_req = NULL; 438*1353Sericheng dld_str_detach(dsp); 439*1353Sericheng dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 440*1353Sericheng 441*1353Sericheng DLD_WAKEUP(dsp); 442*1353Sericheng } 443*1353Sericheng 444269Sericheng /*ARGSUSED*/ 445269Sericheng static boolean_t 446269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4470Sstevel@tonic-gate { 448269Sericheng queue_t *q = dsp->ds_wq; 449269Sericheng t_uscalar_t dl_err; 4500Sstevel@tonic-gate 451269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 4520Sstevel@tonic-gate 453269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 454269Sericheng dl_err = DL_BADPRIM; 455269Sericheng goto failed; 456269Sericheng } 4570Sstevel@tonic-gate 458269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 459269Sericheng dl_err = DL_OUTSTATE; 460269Sericheng goto failed; 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate 463269Sericheng if (dsp->ds_style == DL_STYLE1) { 464269Sericheng dl_err = DL_BADPRIM; 465269Sericheng goto failed; 466269Sericheng } 467269Sericheng 468269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 469269Sericheng 470269Sericheng /* 471269Sericheng * Complete the detach when the driver is single-threaded. 472269Sericheng */ 473269Sericheng mutex_enter(&dsp->ds_thr_lock); 474*1353Sericheng ASSERT(dsp->ds_pending_req == NULL); 475*1353Sericheng dsp->ds_pending_req = mp; 476*1353Sericheng dsp->ds_pending_op = proto_process_detach_req; 477*1353Sericheng dsp->ds_pending_cnt++; 478269Sericheng mutex_exit(&dsp->ds_thr_lock); 479269Sericheng rw_exit(&dsp->ds_lock); 480269Sericheng 481269Sericheng return (B_TRUE); 482269Sericheng failed: 483269Sericheng rw_exit(&dsp->ds_lock); 484269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 485269Sericheng return (B_FALSE); 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate /* 489269Sericheng * DL_BIND_REQ 4900Sstevel@tonic-gate */ 491269Sericheng static boolean_t 492269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4930Sstevel@tonic-gate { 494269Sericheng dl_bind_req_t *dlp = (dl_bind_req_t *)udlp; 495269Sericheng int err = 0; 496269Sericheng uint8_t addr[MAXADDRLEN]; 497269Sericheng uint_t addr_length; 498269Sericheng t_uscalar_t dl_err; 499269Sericheng t_scalar_t sap; 500269Sericheng queue_t *q = dsp->ds_wq; 501269Sericheng 502269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 503269Sericheng dl_err = DL_BADPRIM; 504269Sericheng goto failed; 505269Sericheng } 506269Sericheng 507269Sericheng if (dlp->dl_xidtest_flg != 0) { 508269Sericheng dl_err = DL_NOAUTO; 509269Sericheng goto failed; 510269Sericheng } 511269Sericheng 512269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 513269Sericheng dl_err = DL_UNSUPPORTED; 514269Sericheng goto failed; 515269Sericheng } 516269Sericheng 517269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 518269Sericheng 519269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 520269Sericheng dl_err = DL_OUTSTATE; 521269Sericheng goto failed; 522269Sericheng } 5230Sstevel@tonic-gate 524269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 525269Sericheng !dls_active_set(dsp->ds_dc)) { 526269Sericheng dl_err = DL_SYSERR; 527269Sericheng err = EBUSY; 528269Sericheng goto failed; 529269Sericheng } 530269Sericheng 531269Sericheng dsp->ds_dlstate = DL_BIND_PENDING; 532269Sericheng /* 533269Sericheng * Set the receive callback. 534269Sericheng */ 535269Sericheng dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ? 536269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 5370Sstevel@tonic-gate 538269Sericheng /* 539269Sericheng * Bind the channel such that it can receive packets. 540269Sericheng */ 541269Sericheng sap = dsp->ds_sap = dlp->dl_sap; 542269Sericheng err = dls_bind(dsp->ds_dc, dlp->dl_sap); 543269Sericheng if (err != 0) { 544269Sericheng switch (err) { 545269Sericheng case EINVAL: 546269Sericheng dl_err = DL_BADADDR; 547269Sericheng err = 0; 548269Sericheng break; 549269Sericheng default: 550269Sericheng dl_err = DL_SYSERR; 551269Sericheng break; 552269Sericheng } 553269Sericheng dsp->ds_dlstate = DL_UNBOUND; 554269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 555269Sericheng dls_active_clear(dsp->ds_dc); 556269Sericheng 5570Sstevel@tonic-gate goto failed; 558269Sericheng } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 5610Sstevel@tonic-gate * Copy in MAC address. 5620Sstevel@tonic-gate */ 5630Sstevel@tonic-gate addr_length = dsp->ds_mip->mi_addr_length; 5640Sstevel@tonic-gate bcopy(dsp->ds_curr_addr, addr, addr_length); 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate /* 5670Sstevel@tonic-gate * Copy in the DLSAP. 5680Sstevel@tonic-gate */ 5690Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 5700Sstevel@tonic-gate addr_length += sizeof (uint16_t); 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 573269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 574269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 5750Sstevel@tonic-gate 576269Sericheng rw_exit(&dsp->ds_lock); 577269Sericheng 578269Sericheng dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0); 579269Sericheng return (B_TRUE); 5800Sstevel@tonic-gate failed: 581269Sericheng rw_exit(&dsp->ds_lock); 582269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 583269Sericheng return (B_FALSE); 5840Sstevel@tonic-gate } 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate /* 587269Sericheng * DL_UNBIND_REQ 5880Sstevel@tonic-gate */ 589269Sericheng /*ARGSUSED*/ 590*1353Sericheng static void 591*1353Sericheng proto_process_unbind_req(void *arg) 5920Sstevel@tonic-gate { 593*1353Sericheng dld_str_t *dsp = arg; 594*1353Sericheng mblk_t *mp; 595269Sericheng 596*1353Sericheng /* 597*1353Sericheng * We don't need to hold locks because no other thread 598*1353Sericheng * would manipulate dsp while it is in a PENDING state. 599*1353Sericheng */ 600*1353Sericheng ASSERT(dsp->ds_pending_req != NULL); 601*1353Sericheng ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING); 602269Sericheng 603269Sericheng /* 604269Sericheng * Flush any remaining packets scheduled for transmission. 605269Sericheng */ 606269Sericheng dld_tx_flush(dsp); 607269Sericheng 608269Sericheng /* 609269Sericheng * Unbind the channel to stop packets being received. 610269Sericheng */ 611269Sericheng dls_unbind(dsp->ds_dc); 612269Sericheng 613269Sericheng /* 614269Sericheng * Disable polling mode, if it is enabled. 615269Sericheng */ 616269Sericheng proto_poll_disable(dsp); 617269Sericheng 618269Sericheng /* 619269Sericheng * Clear the receive callback. 620269Sericheng */ 621269Sericheng dls_rx_set(dsp->ds_dc, NULL, NULL); 622269Sericheng 623269Sericheng /* 624269Sericheng * Set the mode back to the default (unitdata). 625269Sericheng */ 626269Sericheng dsp->ds_mode = DLD_UNITDATA; 627269Sericheng 6281184Skrgopi /* 6291184Skrgopi * If soft rings were enabled, the workers 630*1353Sericheng * should be quiesced. We cannot check for 6311184Skrgopi * ds_soft_ring flag because 6321184Skrgopi * proto_soft_ring_disable() called from 6331184Skrgopi * proto_capability_req() would have reset it. 6341184Skrgopi */ 635*1353Sericheng if (dls_soft_ring_workers(dsp->ds_dc)) 636*1353Sericheng dls_soft_ring_disable(dsp->ds_dc); 637*1353Sericheng 638*1353Sericheng mp = dsp->ds_pending_req; 639*1353Sericheng dsp->ds_pending_req = NULL; 640*1353Sericheng dsp->ds_dlstate = DL_UNBOUND; 641*1353Sericheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 642*1353Sericheng 643*1353Sericheng DLD_WAKEUP(dsp); 644*1353Sericheng } 645*1353Sericheng 646*1353Sericheng /*ARGSUSED*/ 647*1353Sericheng static boolean_t 648*1353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 649*1353Sericheng { 650*1353Sericheng queue_t *q = dsp->ds_wq; 651*1353Sericheng t_uscalar_t dl_err; 652*1353Sericheng 653*1353Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 654*1353Sericheng 655*1353Sericheng if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 656*1353Sericheng dl_err = DL_BADPRIM; 657*1353Sericheng goto failed; 6581184Skrgopi } 6591184Skrgopi 660*1353Sericheng if (dsp->ds_dlstate != DL_IDLE) { 661*1353Sericheng dl_err = DL_OUTSTATE; 662*1353Sericheng goto failed; 663*1353Sericheng } 664*1353Sericheng 665*1353Sericheng dsp->ds_dlstate = DL_UNBIND_PENDING; 666*1353Sericheng 667*1353Sericheng mutex_enter(&dsp->ds_thr_lock); 668*1353Sericheng ASSERT(dsp->ds_pending_req == NULL); 669*1353Sericheng dsp->ds_pending_req = mp; 670*1353Sericheng dsp->ds_pending_op = proto_process_unbind_req; 671*1353Sericheng dsp->ds_pending_cnt++; 672*1353Sericheng mutex_exit(&dsp->ds_thr_lock); 673269Sericheng rw_exit(&dsp->ds_lock); 674269Sericheng 675269Sericheng return (B_TRUE); 676269Sericheng failed: 677269Sericheng rw_exit(&dsp->ds_lock); 678269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 679269Sericheng return (B_FALSE); 6800Sstevel@tonic-gate } 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate /* 683269Sericheng * DL_PROMISCON_REQ 6840Sstevel@tonic-gate */ 685269Sericheng static boolean_t 686269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6870Sstevel@tonic-gate { 688269Sericheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp; 689269Sericheng int err = 0; 690269Sericheng t_uscalar_t dl_err; 691269Sericheng uint32_t promisc_saved; 692269Sericheng queue_t *q = dsp->ds_wq; 693269Sericheng 694269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 695269Sericheng dl_err = DL_BADPRIM; 696269Sericheng goto failed; 697269Sericheng } 698269Sericheng 699269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 700269Sericheng 701269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 702269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 703269Sericheng dl_err = DL_OUTSTATE; 7040Sstevel@tonic-gate goto failed; 705269Sericheng } 7060Sstevel@tonic-gate 707269Sericheng promisc_saved = dsp->ds_promisc; 708269Sericheng switch (dlp->dl_level) { 709269Sericheng case DL_PROMISC_SAP: 710269Sericheng dsp->ds_promisc |= DLS_PROMISC_SAP; 711269Sericheng break; 712269Sericheng 713269Sericheng case DL_PROMISC_MULTI: 714269Sericheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 715269Sericheng break; 716269Sericheng 717269Sericheng case DL_PROMISC_PHYS: 718269Sericheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 719269Sericheng break; 720269Sericheng 721269Sericheng default: 722269Sericheng dl_err = DL_NOTSUPPORTED; 723269Sericheng goto failed; 724269Sericheng } 7250Sstevel@tonic-gate 726269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 727269Sericheng !dls_active_set(dsp->ds_dc)) { 728269Sericheng dsp->ds_promisc = promisc_saved; 729269Sericheng dl_err = DL_SYSERR; 730269Sericheng err = EBUSY; 731269Sericheng goto failed; 732269Sericheng } 733269Sericheng 734269Sericheng /* 735269Sericheng * Adjust channel promiscuity. 736269Sericheng */ 737269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 738269Sericheng if (err != 0) { 739269Sericheng dl_err = DL_SYSERR; 740269Sericheng dsp->ds_promisc = promisc_saved; 741269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 742269Sericheng dls_active_clear(dsp->ds_dc); 743269Sericheng 744269Sericheng goto failed; 745269Sericheng } 746269Sericheng 747269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 748269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 749269Sericheng 750269Sericheng rw_exit(&dsp->ds_lock); 751269Sericheng dlokack(q, mp, DL_PROMISCON_REQ); 752269Sericheng return (B_TRUE); 7530Sstevel@tonic-gate failed: 754269Sericheng rw_exit(&dsp->ds_lock); 755269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 756269Sericheng return (B_FALSE); 7570Sstevel@tonic-gate } 7580Sstevel@tonic-gate 7590Sstevel@tonic-gate /* 760269Sericheng * DL_PROMISCOFF_REQ 7610Sstevel@tonic-gate */ 762269Sericheng static boolean_t 763269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 7640Sstevel@tonic-gate { 765269Sericheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp; 766269Sericheng int err = 0; 767269Sericheng t_uscalar_t dl_err; 768269Sericheng uint32_t promisc_saved; 769269Sericheng queue_t *q = dsp->ds_wq; 770269Sericheng 771269Sericheng 772269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 773269Sericheng dl_err = DL_BADPRIM; 7740Sstevel@tonic-gate goto failed; 775269Sericheng } 7760Sstevel@tonic-gate 777269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 7780Sstevel@tonic-gate 779269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 780269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 781269Sericheng dl_err = DL_OUTSTATE; 7820Sstevel@tonic-gate goto failed; 783269Sericheng } 7840Sstevel@tonic-gate 785269Sericheng promisc_saved = dsp->ds_promisc; 786269Sericheng switch (dlp->dl_level) { 787269Sericheng case DL_PROMISC_SAP: 788269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 789269Sericheng dl_err = DL_NOTENAB; 790269Sericheng goto failed; 791269Sericheng } 792269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 7930Sstevel@tonic-gate break; 7940Sstevel@tonic-gate 795269Sericheng case DL_PROMISC_MULTI: 796269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 797269Sericheng dl_err = DL_NOTENAB; 798269Sericheng goto failed; 799269Sericheng } 800269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 801269Sericheng break; 802269Sericheng 803269Sericheng case DL_PROMISC_PHYS: 804269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 805269Sericheng dl_err = DL_NOTENAB; 806269Sericheng goto failed; 807269Sericheng } 808269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 8090Sstevel@tonic-gate break; 8100Sstevel@tonic-gate 8110Sstevel@tonic-gate default: 812269Sericheng dl_err = DL_NOTSUPPORTED; 813269Sericheng goto failed; 814269Sericheng } 815269Sericheng 816269Sericheng /* 817269Sericheng * Adjust channel promiscuity. 818269Sericheng */ 819269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 820269Sericheng if (err != 0) { 821269Sericheng dsp->ds_promisc = promisc_saved; 8220Sstevel@tonic-gate dl_err = DL_SYSERR; 823269Sericheng goto failed; 824269Sericheng } 825269Sericheng 826269Sericheng rw_exit(&dsp->ds_lock); 827269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 828269Sericheng return (B_TRUE); 829269Sericheng failed: 830269Sericheng rw_exit(&dsp->ds_lock); 831269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 832269Sericheng return (B_FALSE); 833269Sericheng } 834269Sericheng 835269Sericheng /* 836269Sericheng * DL_ENABMULTI_REQ 837269Sericheng */ 838269Sericheng static boolean_t 839269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 840269Sericheng { 841269Sericheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp; 842269Sericheng int err = 0; 843269Sericheng t_uscalar_t dl_err; 844269Sericheng queue_t *q = dsp->ds_wq; 845269Sericheng 846269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 847269Sericheng 848269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 849269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 850269Sericheng dl_err = DL_OUTSTATE; 851269Sericheng goto failed; 852269Sericheng } 853269Sericheng 854269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 855269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 856269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 857269Sericheng dl_err = DL_BADPRIM; 858269Sericheng goto failed; 859269Sericheng } 860269Sericheng 861269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 862269Sericheng !dls_active_set(dsp->ds_dc)) { 863269Sericheng dl_err = DL_SYSERR; 864269Sericheng err = EBUSY; 865269Sericheng goto failed; 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate 868269Sericheng err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 869269Sericheng if (err != 0) { 870269Sericheng switch (err) { 871269Sericheng case EINVAL: 872269Sericheng dl_err = DL_BADADDR; 873269Sericheng err = 0; 874269Sericheng break; 875269Sericheng case ENOSPC: 876269Sericheng dl_err = DL_TOOMANY; 877269Sericheng err = 0; 878269Sericheng break; 879269Sericheng default: 880269Sericheng dl_err = DL_SYSERR; 881269Sericheng break; 882269Sericheng } 883269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 884269Sericheng dls_active_clear(dsp->ds_dc); 885269Sericheng 886269Sericheng goto failed; 887269Sericheng } 888269Sericheng 889269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 890269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 891269Sericheng 892269Sericheng rw_exit(&dsp->ds_lock); 893269Sericheng dlokack(q, mp, DL_ENABMULTI_REQ); 894269Sericheng return (B_TRUE); 895269Sericheng failed: 896269Sericheng rw_exit(&dsp->ds_lock); 897269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 898269Sericheng return (B_FALSE); 899269Sericheng } 900269Sericheng 901269Sericheng /* 902269Sericheng * DL_DISABMULTI_REQ 903269Sericheng */ 904269Sericheng static boolean_t 905269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 906269Sericheng { 907269Sericheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp; 908269Sericheng int err = 0; 909269Sericheng t_uscalar_t dl_err; 910269Sericheng queue_t *q = dsp->ds_wq; 911269Sericheng 912269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 913269Sericheng 914269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 915269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 916269Sericheng dl_err = DL_OUTSTATE; 917269Sericheng goto failed; 918269Sericheng } 919269Sericheng 920269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 921269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 922269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 923269Sericheng dl_err = DL_BADPRIM; 924269Sericheng goto failed; 925269Sericheng } 926269Sericheng 927269Sericheng err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 928269Sericheng if (err != 0) { 929269Sericheng switch (err) { 930269Sericheng case EINVAL: 931269Sericheng dl_err = DL_BADADDR; 932269Sericheng err = 0; 933269Sericheng break; 934269Sericheng 935269Sericheng case ENOENT: 936269Sericheng dl_err = DL_NOTENAB; 937269Sericheng err = 0; 938269Sericheng break; 939269Sericheng 940269Sericheng default: 941269Sericheng dl_err = DL_SYSERR; 942269Sericheng break; 943269Sericheng } 944269Sericheng goto failed; 945269Sericheng } 946269Sericheng 947269Sericheng rw_exit(&dsp->ds_lock); 948269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 949269Sericheng return (B_TRUE); 950269Sericheng failed: 951269Sericheng rw_exit(&dsp->ds_lock); 952269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 953269Sericheng return (B_FALSE); 9540Sstevel@tonic-gate } 9550Sstevel@tonic-gate 9560Sstevel@tonic-gate /* 957269Sericheng * DL_PHYS_ADDR_REQ 9580Sstevel@tonic-gate */ 959269Sericheng static boolean_t 960269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 9610Sstevel@tonic-gate { 962269Sericheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp; 963269Sericheng queue_t *q = dsp->ds_wq; 964269Sericheng t_uscalar_t dl_err; 965269Sericheng char *addr; 966269Sericheng uint_t addr_length; 967269Sericheng 968269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 969269Sericheng 970269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 971269Sericheng dl_err = DL_BADPRIM; 972269Sericheng goto failed; 973269Sericheng } 974269Sericheng 975269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 976269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 977269Sericheng dl_err = DL_OUTSTATE; 978269Sericheng goto failed; 979269Sericheng } 9800Sstevel@tonic-gate 981269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 982269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 983269Sericheng dl_err = DL_UNSUPPORTED; 9840Sstevel@tonic-gate goto failed; 985269Sericheng } 9860Sstevel@tonic-gate 987269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 988269Sericheng addr = kmem_alloc(addr_length, KM_NOSLEEP); 989269Sericheng if (addr == NULL) { 990269Sericheng rw_exit(&dsp->ds_lock); 991269Sericheng merror(q, mp, ENOSR); 992269Sericheng return (B_FALSE); 993269Sericheng } 9940Sstevel@tonic-gate 995269Sericheng /* 996269Sericheng * Copy out the address before we drop the lock; we don't 997269Sericheng * want to call dlphysaddrack() while holding ds_lock. 998269Sericheng */ 999269Sericheng bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ? 1000269Sericheng dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length); 1001269Sericheng 1002269Sericheng rw_exit(&dsp->ds_lock); 1003269Sericheng dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 1004269Sericheng kmem_free(addr, addr_length); 1005269Sericheng return (B_TRUE); 10060Sstevel@tonic-gate failed: 1007269Sericheng rw_exit(&dsp->ds_lock); 1008269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 1009269Sericheng return (B_FALSE); 1010269Sericheng } 10110Sstevel@tonic-gate 1012269Sericheng /* 1013269Sericheng * DL_SET_PHYS_ADDR_REQ 1014269Sericheng */ 1015269Sericheng static boolean_t 1016269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1017269Sericheng { 1018269Sericheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp; 1019269Sericheng int err = 0; 1020269Sericheng t_uscalar_t dl_err; 1021269Sericheng queue_t *q = dsp->ds_wq; 10220Sstevel@tonic-gate 1023269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1024269Sericheng 1025269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1026269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1027269Sericheng dl_err = DL_OUTSTATE; 1028269Sericheng goto failed; 1029269Sericheng } 1030269Sericheng 1031269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 1032269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 1033269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 1034269Sericheng dl_err = DL_BADPRIM; 1035269Sericheng goto failed; 10360Sstevel@tonic-gate } 10370Sstevel@tonic-gate 1038269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 1039269Sericheng !dls_active_set(dsp->ds_dc)) { 1040269Sericheng dl_err = DL_SYSERR; 1041269Sericheng err = EBUSY; 1042269Sericheng goto failed; 1043269Sericheng } 1044269Sericheng 1045269Sericheng err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); 1046269Sericheng if (err != 0) { 1047269Sericheng switch (err) { 1048269Sericheng case EINVAL: 1049269Sericheng dl_err = DL_BADADDR; 1050269Sericheng err = 0; 1051269Sericheng break; 1052269Sericheng 1053269Sericheng default: 1054269Sericheng dl_err = DL_SYSERR; 1055269Sericheng break; 1056269Sericheng } 1057269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1058269Sericheng dls_active_clear(dsp->ds_dc); 1059269Sericheng 1060269Sericheng goto failed; 1061269Sericheng } 1062269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1063269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 1064269Sericheng 1065269Sericheng rw_exit(&dsp->ds_lock); 1066269Sericheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 1067269Sericheng return (B_TRUE); 1068269Sericheng failed: 1069269Sericheng rw_exit(&dsp->ds_lock); 1070269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 1071269Sericheng return (B_FALSE); 1072269Sericheng } 1073269Sericheng 1074269Sericheng /* 1075269Sericheng * DL_UDQOS_REQ 1076269Sericheng */ 1077269Sericheng static boolean_t 1078269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1079269Sericheng { 1080269Sericheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp; 1081269Sericheng dl_qos_cl_sel1_t *selp; 1082269Sericheng int off, len; 1083269Sericheng t_uscalar_t dl_err; 1084269Sericheng queue_t *q = dsp->ds_wq; 1085269Sericheng 1086269Sericheng off = dlp->dl_qos_offset; 1087269Sericheng len = dlp->dl_qos_length; 1088269Sericheng 1089269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 1090269Sericheng dl_err = DL_BADPRIM; 1091269Sericheng goto failed; 1092269Sericheng } 1093269Sericheng 1094269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 1095269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 1096269Sericheng dl_err = DL_BADQOSTYPE; 1097269Sericheng goto failed; 1098269Sericheng } 1099269Sericheng 1100269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1101269Sericheng 1102269Sericheng if (dsp->ds_vid == VLAN_ID_NONE || 1103269Sericheng selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 1104269Sericheng selp->dl_priority < 0) { 1105269Sericheng dl_err = DL_BADQOSPARAM; 1106269Sericheng goto failed; 1107269Sericheng } 1108269Sericheng 1109269Sericheng dsp->ds_pri = selp->dl_priority; 1110269Sericheng 1111269Sericheng rw_exit(&dsp->ds_lock); 1112269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 1113269Sericheng return (B_TRUE); 1114269Sericheng failed: 1115269Sericheng rw_exit(&dsp->ds_lock); 1116269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 1117269Sericheng return (B_FALSE); 11180Sstevel@tonic-gate } 11190Sstevel@tonic-gate 11201184Skrgopi static boolean_t 11211184Skrgopi check_ip_above(queue_t *q) 11221184Skrgopi { 11231184Skrgopi queue_t *next_q; 11241184Skrgopi boolean_t ret = B_TRUE; 11251184Skrgopi 11261184Skrgopi claimstr(q); 11271184Skrgopi next_q = q->q_next; 11281184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 11291184Skrgopi ret = B_FALSE; 11301184Skrgopi releasestr(q); 11311184Skrgopi return (ret); 11321184Skrgopi } 11331184Skrgopi 11340Sstevel@tonic-gate /* 1135269Sericheng * DL_CAPABILITY_REQ 11360Sstevel@tonic-gate */ 1137269Sericheng /*ARGSUSED*/ 1138269Sericheng static boolean_t 1139269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 11400Sstevel@tonic-gate { 1141269Sericheng dl_capability_req_t *dlp = (dl_capability_req_t *)udlp; 1142269Sericheng dl_capability_sub_t *sp; 1143269Sericheng size_t size, len; 1144269Sericheng offset_t off, end; 1145269Sericheng t_uscalar_t dl_err; 1146269Sericheng queue_t *q = dsp->ds_wq; 1147269Sericheng boolean_t upgraded; 1148269Sericheng 1149269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1150269Sericheng 1151269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1152269Sericheng dl_err = DL_BADPRIM; 1153269Sericheng goto failed; 1154269Sericheng } 1155269Sericheng 1156269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1157269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1158269Sericheng dl_err = DL_OUTSTATE; 1159269Sericheng goto failed; 1160269Sericheng } 1161269Sericheng 1162269Sericheng /* 1163269Sericheng * This request is overloaded. If there are no requested capabilities 1164269Sericheng * then we just want to acknowledge with all the capabilities we 1165269Sericheng * support. Otherwise we enable the set of capabilities requested. 1166269Sericheng */ 1167269Sericheng if (dlp->dl_sub_length == 0) { 1168269Sericheng /* callee drops lock */ 1169269Sericheng return (proto_capability_advertise(dsp, mp)); 1170269Sericheng } 1171269Sericheng 1172269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1173269Sericheng dl_err = DL_BADPRIM; 1174269Sericheng goto failed; 1175269Sericheng } 1176269Sericheng 1177269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1178269Sericheng 1179269Sericheng off = dlp->dl_sub_offset; 1180269Sericheng len = dlp->dl_sub_length; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate /* 1183269Sericheng * Walk the list of capabilities to be enabled. 11840Sstevel@tonic-gate */ 1185269Sericheng upgraded = B_FALSE; 1186269Sericheng for (end = off + len; off < end; ) { 1187269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1188269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1189269Sericheng 1190269Sericheng if (off + size > end || 1191269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1192269Sericheng dl_err = DL_BADPRIM; 1193269Sericheng goto failed; 1194269Sericheng } 1195269Sericheng 1196269Sericheng switch (sp->dl_cap) { 1197269Sericheng /* 1198269Sericheng * TCP/IP checksum offload to hardware. 1199269Sericheng */ 1200269Sericheng case DL_CAPAB_HCKSUM: { 1201269Sericheng dl_capab_hcksum_t *hcksump; 1202269Sericheng dl_capab_hcksum_t hcksum; 1203269Sericheng 1204269Sericheng ASSERT(dsp->ds_mip->mi_cksum != 0); 1205269Sericheng 1206269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1207269Sericheng /* 1208269Sericheng * Copy for alignment. 1209269Sericheng */ 1210269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1211269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1212269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1213269Sericheng break; 1214269Sericheng } 1215269Sericheng 1216269Sericheng /* 1217269Sericheng * IP polling interface. 1218269Sericheng */ 1219269Sericheng case DL_CAPAB_POLL: { 12201184Skrgopi dl_capab_dls_t *pollp; 12211184Skrgopi dl_capab_dls_t poll; 1222269Sericheng 12231184Skrgopi pollp = (dl_capab_dls_t *)&sp[1]; 1224269Sericheng /* 1225269Sericheng * Copy for alignment. 1226269Sericheng */ 12271184Skrgopi bcopy(pollp, &poll, sizeof (dl_capab_dls_t)); 1228269Sericheng 1229269Sericheng /* 1230269Sericheng * We need to become writer before enabling and/or 1231269Sericheng * disabling the polling interface. If we couldn' 1232269Sericheng * upgrade, check state again after re-acquiring the 1233269Sericheng * lock to make sure we can proceed. 1234269Sericheng */ 1235269Sericheng if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 1236269Sericheng rw_exit(&dsp->ds_lock); 1237269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1238269Sericheng 1239269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1240269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1241269Sericheng dl_err = DL_OUTSTATE; 1242269Sericheng goto failed; 1243269Sericheng } 1244269Sericheng } 1245269Sericheng upgraded = B_TRUE; 1246269Sericheng 12471184Skrgopi switch (poll.dls_flags) { 1248269Sericheng default: 1249269Sericheng /*FALLTHRU*/ 1250269Sericheng case POLL_DISABLE: 1251269Sericheng proto_poll_disable(dsp); 1252269Sericheng break; 1253269Sericheng 1254269Sericheng case POLL_ENABLE: 1255269Sericheng ASSERT(!(dld_opt & DLD_OPT_NO_POLL)); 1256269Sericheng 1257269Sericheng /* 1258269Sericheng * Make sure polling is disabled. 1259269Sericheng */ 1260269Sericheng proto_poll_disable(dsp); 1261269Sericheng 1262269Sericheng /* 1263269Sericheng * Now attempt enable it. 1264269Sericheng */ 12651184Skrgopi if (check_ip_above(dsp->ds_rq) && 12661184Skrgopi proto_poll_enable(dsp, &poll)) { 12671184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 12681184Skrgopi poll.dls_flags = POLL_ENABLE; 12691184Skrgopi } 1270269Sericheng break; 1271269Sericheng } 1272269Sericheng 12731184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 12741184Skrgopi bcopy(&poll, pollp, sizeof (dl_capab_dls_t)); 12751184Skrgopi break; 12761184Skrgopi } 12771184Skrgopi case DL_CAPAB_SOFT_RING: { 12781184Skrgopi dl_capab_dls_t *soft_ringp; 12791184Skrgopi dl_capab_dls_t soft_ring; 12801184Skrgopi 12811184Skrgopi soft_ringp = (dl_capab_dls_t *)&sp[1]; 12821184Skrgopi /* 12831184Skrgopi * Copy for alignment. 12841184Skrgopi */ 12851184Skrgopi bcopy(soft_ringp, &soft_ring, 12861184Skrgopi sizeof (dl_capab_dls_t)); 12871184Skrgopi 12881184Skrgopi /* 12891184Skrgopi * We need to become writer before enabling and/or 12901184Skrgopi * disabling the soft_ring interface. If we couldn' 12911184Skrgopi * upgrade, check state again after re-acquiring the 12921184Skrgopi * lock to make sure we can proceed. 12931184Skrgopi */ 12941184Skrgopi if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 12951184Skrgopi rw_exit(&dsp->ds_lock); 12961184Skrgopi rw_enter(&dsp->ds_lock, RW_WRITER); 12971184Skrgopi 12981184Skrgopi if (dsp->ds_dlstate == DL_UNATTACHED || 12991184Skrgopi DL_ACK_PENDING(dsp->ds_dlstate)) { 13001184Skrgopi dl_err = DL_OUTSTATE; 13011184Skrgopi goto failed; 13021184Skrgopi } 13031184Skrgopi } 13041184Skrgopi upgraded = B_TRUE; 13051184Skrgopi 13061184Skrgopi switch (soft_ring.dls_flags) { 13071184Skrgopi default: 13081184Skrgopi /*FALLTHRU*/ 13091184Skrgopi case SOFT_RING_DISABLE: 13101184Skrgopi proto_soft_ring_disable(dsp); 13111184Skrgopi break; 13121184Skrgopi 13131184Skrgopi case SOFT_RING_ENABLE: 13141184Skrgopi /* 13151184Skrgopi * Make sure soft_ring is disabled. 13161184Skrgopi */ 13171184Skrgopi proto_soft_ring_disable(dsp); 13181184Skrgopi 13191184Skrgopi /* 13201184Skrgopi * Now attempt enable it. 13211184Skrgopi */ 13221184Skrgopi if (check_ip_above(dsp->ds_rq) && 13231184Skrgopi proto_soft_ring_enable(dsp, &soft_ring)) { 13241184Skrgopi bzero(&soft_ring, 13251184Skrgopi sizeof (dl_capab_dls_t)); 13261184Skrgopi soft_ring.dls_flags = 13271184Skrgopi SOFT_RING_ENABLE; 13281184Skrgopi } else { 13291184Skrgopi bzero(&soft_ring, 13301184Skrgopi sizeof (dl_capab_dls_t)); 13311184Skrgopi soft_ring.dls_flags = 13321184Skrgopi SOFT_RING_DISABLE; 13331184Skrgopi } 13341184Skrgopi break; 13351184Skrgopi } 13361184Skrgopi 13371184Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 13381184Skrgopi bcopy(&soft_ring, soft_ringp, 13391184Skrgopi sizeof (dl_capab_dls_t)); 1340269Sericheng break; 1341269Sericheng } 1342269Sericheng default: 1343269Sericheng break; 1344269Sericheng } 1345269Sericheng 1346269Sericheng off += size; 1347269Sericheng } 1348269Sericheng rw_exit(&dsp->ds_lock); 1349269Sericheng qreply(q, mp); 1350269Sericheng return (B_TRUE); 1351269Sericheng failed: 1352269Sericheng rw_exit(&dsp->ds_lock); 1353269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 1354269Sericheng return (B_FALSE); 13550Sstevel@tonic-gate } 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate /* 1358269Sericheng * DL_NOTIFY_REQ 13590Sstevel@tonic-gate */ 1360269Sericheng static boolean_t 1361269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 13620Sstevel@tonic-gate { 1363269Sericheng dl_notify_req_t *dlp = (dl_notify_req_t *)udlp; 1364269Sericheng t_uscalar_t dl_err; 1365269Sericheng queue_t *q = dsp->ds_wq; 1366269Sericheng uint_t note = 1367269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1368269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1369269Sericheng DL_NOTE_PHYS_ADDR | 1370269Sericheng DL_NOTE_LINK_UP | 1371269Sericheng DL_NOTE_LINK_DOWN | 1372269Sericheng DL_NOTE_CAPAB_RENEG; 13730Sstevel@tonic-gate 1374269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1375269Sericheng dl_err = DL_BADPRIM; 1376269Sericheng goto failed; 1377269Sericheng } 13780Sstevel@tonic-gate 1379269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1380269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1381269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1382269Sericheng dl_err = DL_OUTSTATE; 1383269Sericheng goto failed; 13840Sstevel@tonic-gate } 13850Sstevel@tonic-gate 1386269Sericheng if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED]) 1387269Sericheng note |= DL_NOTE_SPEED; 1388269Sericheng 1389269Sericheng /* 1390269Sericheng * Cache the notifications that are being enabled. 1391269Sericheng */ 1392269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1393269Sericheng rw_exit(&dsp->ds_lock); 1394269Sericheng /* 1395269Sericheng * The ACK carries all notifications regardless of which set is 1396269Sericheng * being enabled. 1397269Sericheng */ 1398269Sericheng dlnotifyack(q, mp, note); 1399269Sericheng 1400269Sericheng /* 1401269Sericheng * Solicit DL_NOTIFY_IND messages for each enabled notification. 1402269Sericheng */ 1403269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1404269Sericheng if (dsp->ds_notifications != 0) { 1405269Sericheng rw_exit(&dsp->ds_lock); 1406269Sericheng dld_str_notify_ind(dsp); 1407269Sericheng } else { 1408269Sericheng rw_exit(&dsp->ds_lock); 1409269Sericheng } 1410269Sericheng return (B_TRUE); 1411269Sericheng failed: 1412269Sericheng rw_exit(&dsp->ds_lock); 1413269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 1414269Sericheng return (B_FALSE); 14150Sstevel@tonic-gate } 14160Sstevel@tonic-gate 14170Sstevel@tonic-gate /* 1418269Sericheng * DL_UINTDATA_REQ 14190Sstevel@tonic-gate */ 1420269Sericheng static boolean_t 1421269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 14220Sstevel@tonic-gate { 1423269Sericheng queue_t *q = dsp->ds_wq; 1424269Sericheng dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)udlp; 1425269Sericheng off_t off; 1426269Sericheng size_t len, size; 1427269Sericheng const uint8_t *addr; 1428269Sericheng uint16_t sap; 1429269Sericheng uint_t addr_length; 1430269Sericheng mblk_t *bp, *cont; 1431269Sericheng uint32_t start, stuff, end, value, flags; 1432269Sericheng t_uscalar_t dl_err; 1433269Sericheng 1434269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1435269Sericheng 1436269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1437269Sericheng dl_err = DL_BADPRIM; 1438269Sericheng goto failed; 1439269Sericheng } 1440269Sericheng 1441269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 1442269Sericheng dl_err = DL_OUTSTATE; 1443269Sericheng goto failed; 1444269Sericheng } 1445269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1446269Sericheng 1447269Sericheng off = dlp->dl_dest_addr_offset; 1448269Sericheng len = dlp->dl_dest_addr_length; 1449269Sericheng 1450269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1451269Sericheng dl_err = DL_BADPRIM; 1452269Sericheng goto failed; 1453269Sericheng } 1454269Sericheng 1455269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1456269Sericheng dl_err = DL_BADADDR; 1457269Sericheng goto failed; 1458269Sericheng } 1459269Sericheng 1460269Sericheng addr = mp->b_rptr + off; 1461269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1462269Sericheng 1463269Sericheng /* 1464269Sericheng * Check the length of the packet and the block types. 1465269Sericheng */ 1466269Sericheng size = 0; 1467269Sericheng cont = mp->b_cont; 1468269Sericheng for (bp = cont; bp != NULL; bp = bp->b_cont) { 1469269Sericheng if (DB_TYPE(bp) != M_DATA) 1470269Sericheng goto baddata; 1471269Sericheng 1472269Sericheng size += MBLKL(bp); 1473269Sericheng } 1474269Sericheng 1475269Sericheng if (size > dsp->ds_mip->mi_sdu_max) 1476269Sericheng goto baddata; 1477269Sericheng 1478269Sericheng /* 1479269Sericheng * Build a packet header. 1480269Sericheng */ 1481269Sericheng if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) { 1482269Sericheng dl_err = DL_BADADDR; 1483269Sericheng goto failed; 1484269Sericheng } 1485269Sericheng 1486269Sericheng /* 1487269Sericheng * We no longer need the M_PROTO header, so free it. 1488269Sericheng */ 1489269Sericheng freeb(mp); 1490269Sericheng 1491269Sericheng /* 1492269Sericheng * Transfer the checksum offload information if it is present. 1493269Sericheng */ 1494269Sericheng hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value, 1495269Sericheng &flags); 1496269Sericheng (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 1497269Sericheng 0); 1498269Sericheng 1499269Sericheng /* 1500269Sericheng * Link the payload onto the new header. 1501269Sericheng */ 1502269Sericheng ASSERT(bp->b_cont == NULL); 1503269Sericheng bp->b_cont = cont; 1504269Sericheng 1505269Sericheng str_mdata_fastpath_put(dsp, bp); 1506269Sericheng rw_exit(&dsp->ds_lock); 1507269Sericheng return (B_TRUE); 1508269Sericheng failed: 1509269Sericheng rw_exit(&dsp->ds_lock); 1510269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1511269Sericheng return (B_FALSE); 1512269Sericheng 1513269Sericheng baddata: 1514269Sericheng rw_exit(&dsp->ds_lock); 1515269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1516269Sericheng return (B_FALSE); 1517269Sericheng } 1518269Sericheng 1519269Sericheng /* 1520269Sericheng * DL_PASSIVE_REQ 1521269Sericheng */ 1522269Sericheng /* ARGSUSED */ 1523269Sericheng static boolean_t 1524269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1525269Sericheng { 1526269Sericheng t_uscalar_t dl_err; 1527269Sericheng 1528269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1529269Sericheng /* 1530269Sericheng * If we've already become active by issuing an active primitive, 1531269Sericheng * then it's too late to try to become passive. 1532269Sericheng */ 1533269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1534269Sericheng dl_err = DL_OUTSTATE; 1535269Sericheng goto failed; 1536269Sericheng } 1537269Sericheng 1538269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1539269Sericheng dl_err = DL_BADPRIM; 1540269Sericheng goto failed; 1541269Sericheng } 1542269Sericheng 1543269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1544269Sericheng rw_exit(&dsp->ds_lock); 1545269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1546269Sericheng return (B_TRUE); 1547269Sericheng failed: 1548269Sericheng rw_exit(&dsp->ds_lock); 1549269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1550269Sericheng return (B_FALSE); 1551269Sericheng } 1552269Sericheng 1553269Sericheng 1554269Sericheng /* 1555269Sericheng * Catch-all handler. 1556269Sericheng */ 1557269Sericheng static boolean_t 1558269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp) 1559269Sericheng { 1560269Sericheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1561269Sericheng return (B_FALSE); 15620Sstevel@tonic-gate } 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate static void 15650Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp) 15660Sstevel@tonic-gate { 15670Sstevel@tonic-gate mac_handle_t mh; 15680Sstevel@tonic-gate 1569*1353Sericheng ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock)); 1570269Sericheng 15710Sstevel@tonic-gate if (!dsp->ds_polling) 15720Sstevel@tonic-gate return; 15730Sstevel@tonic-gate 15740Sstevel@tonic-gate /* 15750Sstevel@tonic-gate * It should be impossible to enable raw mode if polling is turned on. 15760Sstevel@tonic-gate */ 15770Sstevel@tonic-gate ASSERT(dsp->ds_mode != DLD_RAW); 15780Sstevel@tonic-gate 15790Sstevel@tonic-gate /* 15800Sstevel@tonic-gate * Reset the resource_add callback. 15810Sstevel@tonic-gate */ 15820Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 15830Sstevel@tonic-gate mac_resource_set(mh, NULL, NULL); 15841184Skrgopi mac_resources(mh); 15850Sstevel@tonic-gate 15860Sstevel@tonic-gate /* 15870Sstevel@tonic-gate * Set receive function back to default. 15880Sstevel@tonic-gate */ 15890Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ? 15900Sstevel@tonic-gate dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate /* 15930Sstevel@tonic-gate * Note that polling is disabled. 15940Sstevel@tonic-gate */ 15950Sstevel@tonic-gate dsp->ds_polling = B_FALSE; 15960Sstevel@tonic-gate } 15970Sstevel@tonic-gate 15980Sstevel@tonic-gate static boolean_t 15991184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp) 16000Sstevel@tonic-gate { 16010Sstevel@tonic-gate mac_handle_t mh; 16020Sstevel@tonic-gate 1603269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16040Sstevel@tonic-gate ASSERT(!dsp->ds_polling); 16050Sstevel@tonic-gate 16060Sstevel@tonic-gate /* 16070Sstevel@tonic-gate * We cannot enable polling if raw mode 16080Sstevel@tonic-gate * has been enabled. 16090Sstevel@tonic-gate */ 16100Sstevel@tonic-gate if (dsp->ds_mode == DLD_RAW) 16110Sstevel@tonic-gate return (B_FALSE); 16120Sstevel@tonic-gate 16130Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate /* 16160Sstevel@tonic-gate * Register resources. 16170Sstevel@tonic-gate */ 16181184Skrgopi mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add, 16191184Skrgopi (void *)pollp->dls_rx_handle); 16200Sstevel@tonic-gate mac_resources(mh); 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate /* 16230Sstevel@tonic-gate * Set the receive function. 16240Sstevel@tonic-gate */ 16251184Skrgopi dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx, 16261184Skrgopi (void *)pollp->dls_rx_handle); 16270Sstevel@tonic-gate 16280Sstevel@tonic-gate /* 16290Sstevel@tonic-gate * Note that polling is enabled. This prevents further DLIOCHDRINFO 16300Sstevel@tonic-gate * ioctls from overwriting the receive function pointer. 16310Sstevel@tonic-gate */ 16320Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 16330Sstevel@tonic-gate return (B_TRUE); 16340Sstevel@tonic-gate } 16350Sstevel@tonic-gate 16361184Skrgopi static void 16371184Skrgopi proto_soft_ring_disable(dld_str_t *dsp) 16381184Skrgopi { 16391184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16401184Skrgopi 16411184Skrgopi if (!dsp->ds_soft_ring) 16421184Skrgopi return; 16431184Skrgopi 16441184Skrgopi /* 16451184Skrgopi * It should be impossible to enable raw mode if soft_ring is turned on. 16461184Skrgopi */ 16471184Skrgopi ASSERT(dsp->ds_mode != DLD_RAW); 16481184Skrgopi proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE); 16491184Skrgopi /* 16501184Skrgopi * Note that fanout is disabled. 16511184Skrgopi */ 16521184Skrgopi dsp->ds_soft_ring = B_FALSE; 16531184Skrgopi } 16541184Skrgopi 16551184Skrgopi static boolean_t 16561184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp) 16571184Skrgopi { 16581184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16591184Skrgopi ASSERT(!dsp->ds_soft_ring); 16601184Skrgopi 16611184Skrgopi /* 16621184Skrgopi * We cannot enable soft_ring if raw mode 16631184Skrgopi * has been enabled. 16641184Skrgopi */ 16651184Skrgopi if (dsp->ds_mode == DLD_RAW) 16661184Skrgopi return (B_FALSE); 16671184Skrgopi 16681184Skrgopi if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE) 16691184Skrgopi return (B_FALSE); 16701184Skrgopi 16711184Skrgopi dsp->ds_soft_ring = B_TRUE; 16721184Skrgopi return (B_TRUE); 16731184Skrgopi } 16741184Skrgopi 16751184Skrgopi static void 16761184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type) 16771184Skrgopi { 16781184Skrgopi dls_rx_t rx; 16791184Skrgopi 16801184Skrgopi if (type == SOFT_RING_NONE) { 16811184Skrgopi rx = (dsp->ds_mode == DLD_FASTPATH) ? 16821184Skrgopi dld_str_rx_fastpath : dld_str_rx_unitdata; 16831184Skrgopi } else { 16841184Skrgopi rx = (dls_rx_t)dls_ether_soft_ring_fanout; 16851184Skrgopi } 16861184Skrgopi dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type); 16871184Skrgopi } 16881184Skrgopi 16890Sstevel@tonic-gate /* 16900Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 16910Sstevel@tonic-gate */ 1692269Sericheng static boolean_t 1693269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 16940Sstevel@tonic-gate { 16950Sstevel@tonic-gate dl_capability_ack_t *dlap; 16960Sstevel@tonic-gate dl_capability_sub_t *dlsp; 16970Sstevel@tonic-gate size_t subsize; 16981184Skrgopi dl_capab_dls_t poll; 16991184Skrgopi dl_capab_dls_t soft_ring; 17000Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 17010Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 17020Sstevel@tonic-gate uint8_t *ptr; 17030Sstevel@tonic-gate uint32_t cksum; 17040Sstevel@tonic-gate boolean_t poll_cap; 1705269Sericheng queue_t *q = dsp->ds_wq; 1706269Sericheng mblk_t *mp1; 1707269Sericheng 1708269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate /* 17110Sstevel@tonic-gate * Initially assume no capabilities. 17120Sstevel@tonic-gate */ 17130Sstevel@tonic-gate subsize = 0; 17140Sstevel@tonic-gate 17151184Skrgopi /* Always advertize soft ring capability for GLDv3 drivers */ 17161184Skrgopi subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dls_t); 17171184Skrgopi 17180Sstevel@tonic-gate /* 17190Sstevel@tonic-gate * Check if polling can be enabled on this interface. 17200Sstevel@tonic-gate * If advertising DL_CAPAB_POLL has not been explicitly disabled 17210Sstevel@tonic-gate * then reserve space for that capability. 17220Sstevel@tonic-gate */ 17230Sstevel@tonic-gate poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) && 17240Sstevel@tonic-gate !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE)); 17250Sstevel@tonic-gate if (poll_cap) { 17260Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17271184Skrgopi sizeof (dl_capab_dls_t); 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate 17300Sstevel@tonic-gate /* 17310Sstevel@tonic-gate * If the MAC interface supports checksum offload then reserve 17320Sstevel@tonic-gate * space for the DL_CAPAB_HCKSUM capability. 17330Sstevel@tonic-gate */ 17340Sstevel@tonic-gate if ((cksum = dsp->ds_mip->mi_cksum) != 0) { 17350Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17360Sstevel@tonic-gate sizeof (dl_capab_hcksum_t); 17370Sstevel@tonic-gate } 17380Sstevel@tonic-gate 17390Sstevel@tonic-gate /* 17400Sstevel@tonic-gate * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then 17410Sstevel@tonic-gate * reserve space for it. 17420Sstevel@tonic-gate */ 17430Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 17440Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17450Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 17460Sstevel@tonic-gate } 17470Sstevel@tonic-gate 17480Sstevel@tonic-gate /* 1749269Sericheng * If there are no capabilities to advertise or if we 1750269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 17510Sstevel@tonic-gate */ 17521184Skrgopi if ((mp1 = reallocb(mp, 1753269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1754269Sericheng rw_exit(&dsp->ds_lock); 1755269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1756269Sericheng return (B_FALSE); 17570Sstevel@tonic-gate } 17580Sstevel@tonic-gate 1759269Sericheng mp = mp1; 1760269Sericheng DB_TYPE(mp) = M_PROTO; 1761269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1762269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 17630Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 17640Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 17650Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 17660Sstevel@tonic-gate dlap->dl_sub_length = subsize; 17670Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 17680Sstevel@tonic-gate 17690Sstevel@tonic-gate /* 17700Sstevel@tonic-gate * IP polling interface. 17710Sstevel@tonic-gate */ 17720Sstevel@tonic-gate if (poll_cap) { 17730Sstevel@tonic-gate /* 1774269Sericheng * Attempt to disable just in case this is a re-negotiation; 1775269Sericheng * we need to become writer before doing so. 17760Sstevel@tonic-gate */ 1777269Sericheng if (!rw_tryupgrade(&dsp->ds_lock)) { 1778269Sericheng rw_exit(&dsp->ds_lock); 1779269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1780269Sericheng } 17810Sstevel@tonic-gate 1782269Sericheng /* 1783269Sericheng * Check if polling state has changed after we re-acquired 1784269Sericheng * the lock above, so that we don't mis-advertise it. 1785269Sericheng */ 1786269Sericheng poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) && 1787269Sericheng !(dld_opt & DLD_OPT_NO_POLL) && 1788269Sericheng (dsp->ds_vid == VLAN_ID_NONE)); 17890Sstevel@tonic-gate 1790269Sericheng if (!poll_cap) { 1791269Sericheng int poll_capab_size; 1792269Sericheng 1793269Sericheng rw_downgrade(&dsp->ds_lock); 1794269Sericheng 1795269Sericheng poll_capab_size = sizeof (dl_capability_sub_t) + 17961184Skrgopi sizeof (dl_capab_dls_t); 17970Sstevel@tonic-gate 1798269Sericheng mp->b_wptr -= poll_capab_size; 1799269Sericheng subsize -= poll_capab_size; 1800269Sericheng dlap->dl_sub_length = subsize; 1801269Sericheng } else { 1802269Sericheng proto_poll_disable(dsp); 1803269Sericheng 1804269Sericheng rw_downgrade(&dsp->ds_lock); 1805269Sericheng 1806269Sericheng dlsp = (dl_capability_sub_t *)ptr; 1807269Sericheng 1808269Sericheng dlsp->dl_cap = DL_CAPAB_POLL; 18091184Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 1810269Sericheng ptr += sizeof (dl_capability_sub_t); 18110Sstevel@tonic-gate 18121184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 18131184Skrgopi poll.dls_version = POLL_VERSION_1; 18141184Skrgopi poll.dls_flags = POLL_CAPABLE; 18151184Skrgopi poll.dls_tx_handle = (uintptr_t)dsp; 18161184Skrgopi poll.dls_tx = (uintptr_t)str_mdata_fastpath_put; 1817269Sericheng 18181184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 18191184Skrgopi bcopy(&poll, ptr, sizeof (dl_capab_dls_t)); 18201184Skrgopi ptr += sizeof (dl_capab_dls_t); 1821269Sericheng } 18220Sstevel@tonic-gate } 18230Sstevel@tonic-gate 1824269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 1825269Sericheng 18261184Skrgopi dlsp = (dl_capability_sub_t *)ptr; 18271184Skrgopi 18281184Skrgopi dlsp->dl_cap = DL_CAPAB_SOFT_RING; 18291184Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 18301184Skrgopi ptr += sizeof (dl_capability_sub_t); 18311184Skrgopi 18321184Skrgopi bzero(&soft_ring, sizeof (dl_capab_dls_t)); 18331184Skrgopi soft_ring.dls_version = SOFT_RING_VERSION_1; 18341184Skrgopi soft_ring.dls_flags = SOFT_RING_CAPABLE; 18351184Skrgopi soft_ring.dls_tx_handle = (uintptr_t)dsp; 18361184Skrgopi soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put; 18371184Skrgopi soft_ring.dls_ring_change_status = 18381184Skrgopi (uintptr_t)proto_change_soft_ring_fanout; 18391184Skrgopi soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind; 18401184Skrgopi soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind; 18411184Skrgopi 18421184Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 18431184Skrgopi bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t)); 18441184Skrgopi ptr += sizeof (dl_capab_dls_t); 18451184Skrgopi 18460Sstevel@tonic-gate /* 18470Sstevel@tonic-gate * TCP/IP checksum offload. 18480Sstevel@tonic-gate */ 18490Sstevel@tonic-gate if (cksum != 0) { 18500Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18510Sstevel@tonic-gate 18520Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 18530Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 18540Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18550Sstevel@tonic-gate 18560Sstevel@tonic-gate bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 18570Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 18580Sstevel@tonic-gate hcksum.hcksum_txflags = cksum; 18590Sstevel@tonic-gate 18600Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 18610Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 18620Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 18630Sstevel@tonic-gate } 18640Sstevel@tonic-gate 18650Sstevel@tonic-gate /* 18660Sstevel@tonic-gate * Zero copy 18670Sstevel@tonic-gate */ 18680Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 18690Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18700Sstevel@tonic-gate 18710Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 18720Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 18730Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 18760Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 18770Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 18780Sstevel@tonic-gate 18790Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 18800Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 18810Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 18820Sstevel@tonic-gate } 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1885269Sericheng 1886269Sericheng rw_exit(&dsp->ds_lock); 1887269Sericheng qreply(q, mp); 1888269Sericheng return (B_TRUE); 18890Sstevel@tonic-gate } 1890