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; 1792311Sseb uint8_t dl_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 1802311Sseb 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 */ 2972311Sseb if (minfop->mi_brdcst_addr != NULL) { 2982311Sseb dlp->dl_brdcst_addr_offset = 2992311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp; 3002311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 3012311Sseb dlp->dl_brdcst_addr_length = addr_length; 3022311Sseb } 3030Sstevel@tonic-gate 3042760Sdg199075 dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 3052760Sdg199075 dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 3060Sstevel@tonic-gate 3072760Sdg199075 rangep->dl_qos_type = DL_QOS_CL_RANGE1; 3082760Sdg199075 rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 3092760Sdg199075 rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 3102760Sdg199075 rangep->dl_protection.dl_min = DL_UNKNOWN; 3112760Sdg199075 rangep->dl_protection.dl_max = DL_UNKNOWN; 3122760Sdg199075 rangep->dl_residual_error = DL_UNKNOWN; 3130Sstevel@tonic-gate 3142760Sdg199075 /* 3152760Sdg199075 * Specify the supported range of priorities. 3162760Sdg199075 */ 3172760Sdg199075 rangep->dl_priority.dl_min = 0; 3182760Sdg199075 rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 3190Sstevel@tonic-gate 3202760Sdg199075 dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 3212760Sdg199075 dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 3220Sstevel@tonic-gate 3232760Sdg199075 selp->dl_qos_type = DL_QOS_CL_SEL1; 3242760Sdg199075 selp->dl_trans_delay = DL_UNKNOWN; 3252760Sdg199075 selp->dl_protection = DL_UNKNOWN; 3262760Sdg199075 selp->dl_residual_error = DL_UNKNOWN; 3272760Sdg199075 3282760Sdg199075 /* 3292760Sdg199075 * Specify the current priority (which can be changed by 3302760Sdg199075 * the DL_UDQOS_REQ primitive). 3312760Sdg199075 */ 3322760Sdg199075 selp->dl_priority = dsp->ds_pri; 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 3350Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 3360Sstevel@tonic-gate /* 3370Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 3380Sstevel@tonic-gate * DLSAP address. 3390Sstevel@tonic-gate */ 3400Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 3412311Sseb if (addr_length > 0) 3422311Sseb bcopy(dsp->ds_curr_addr, addr, addr_length); 3430Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3440Sstevel@tonic-gate } 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate done: 3470Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3480Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3490Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3500Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3510Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3520Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3530Sstevel@tonic-gate 354269Sericheng rw_exit(&dsp->ds_lock); 355269Sericheng 356269Sericheng qreply(q, mp); 357269Sericheng return (B_TRUE); 358269Sericheng } 359269Sericheng 360269Sericheng /* 361269Sericheng * DL_ATTACH_REQ 362269Sericheng */ 363269Sericheng static boolean_t 364269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 365269Sericheng { 366269Sericheng dl_attach_req_t *dlp = (dl_attach_req_t *)udlp; 367269Sericheng int err = 0; 368269Sericheng t_uscalar_t dl_err; 369269Sericheng queue_t *q = dsp->ds_wq; 370269Sericheng 371269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 372269Sericheng 373269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 374269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 375269Sericheng dl_err = DL_BADPRIM; 376269Sericheng goto failed; 377269Sericheng } 378269Sericheng 379269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 380269Sericheng dl_err = DL_OUTSTATE; 381269Sericheng goto failed; 382269Sericheng } 383269Sericheng 384269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 385269Sericheng 386269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 387269Sericheng if (err != 0) { 388269Sericheng switch (err) { 389269Sericheng case ENOENT: 390269Sericheng dl_err = DL_BADPPA; 391269Sericheng err = 0; 392269Sericheng break; 393269Sericheng default: 394269Sericheng dl_err = DL_SYSERR; 395269Sericheng break; 396269Sericheng } 397269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 398269Sericheng goto failed; 399269Sericheng } 400269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 401269Sericheng rw_exit(&dsp->ds_lock); 402269Sericheng 403269Sericheng dlokack(q, mp, DL_ATTACH_REQ); 404269Sericheng return (B_TRUE); 405269Sericheng failed: 406269Sericheng rw_exit(&dsp->ds_lock); 407269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 408269Sericheng return (B_FALSE); 4090Sstevel@tonic-gate } 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate /* 412269Sericheng * DL_DETACH_REQ 4130Sstevel@tonic-gate */ 4141353Sericheng static void 4151353Sericheng proto_process_detach_req(void *arg) 4161353Sericheng { 4171353Sericheng dld_str_t *dsp = arg; 4181353Sericheng mblk_t *mp; 4191353Sericheng 4201353Sericheng /* 4211353Sericheng * We don't need to hold locks because no other thread 4221353Sericheng * would manipulate dsp while it is in a PENDING state. 4231353Sericheng */ 4241353Sericheng ASSERT(dsp->ds_pending_req != NULL); 4251353Sericheng ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING); 4261353Sericheng 4271353Sericheng mp = dsp->ds_pending_req; 4281353Sericheng dsp->ds_pending_req = NULL; 4291353Sericheng dld_str_detach(dsp); 4301353Sericheng dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 4311353Sericheng 4321353Sericheng DLD_WAKEUP(dsp); 4331353Sericheng } 4341353Sericheng 435269Sericheng /*ARGSUSED*/ 436269Sericheng static boolean_t 437269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4380Sstevel@tonic-gate { 439269Sericheng queue_t *q = dsp->ds_wq; 440269Sericheng t_uscalar_t dl_err; 4410Sstevel@tonic-gate 442269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 4430Sstevel@tonic-gate 444269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 445269Sericheng dl_err = DL_BADPRIM; 446269Sericheng goto failed; 447269Sericheng } 4480Sstevel@tonic-gate 449269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 450269Sericheng dl_err = DL_OUTSTATE; 451269Sericheng goto failed; 4520Sstevel@tonic-gate } 4530Sstevel@tonic-gate 454269Sericheng if (dsp->ds_style == DL_STYLE1) { 455269Sericheng dl_err = DL_BADPRIM; 456269Sericheng goto failed; 457269Sericheng } 458269Sericheng 459269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 460269Sericheng 461269Sericheng /* 462269Sericheng * Complete the detach when the driver is single-threaded. 463269Sericheng */ 464269Sericheng mutex_enter(&dsp->ds_thr_lock); 4651353Sericheng ASSERT(dsp->ds_pending_req == NULL); 4661353Sericheng dsp->ds_pending_req = mp; 4671353Sericheng dsp->ds_pending_op = proto_process_detach_req; 4681353Sericheng dsp->ds_pending_cnt++; 469269Sericheng mutex_exit(&dsp->ds_thr_lock); 470269Sericheng rw_exit(&dsp->ds_lock); 471269Sericheng 472269Sericheng return (B_TRUE); 473269Sericheng failed: 474269Sericheng rw_exit(&dsp->ds_lock); 475269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 476269Sericheng return (B_FALSE); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 480269Sericheng * DL_BIND_REQ 4810Sstevel@tonic-gate */ 482269Sericheng static boolean_t 483269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4840Sstevel@tonic-gate { 485269Sericheng dl_bind_req_t *dlp = (dl_bind_req_t *)udlp; 486269Sericheng int err = 0; 4873037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 4883037Syz147064 uint_t dlsap_addr_length; 489269Sericheng t_uscalar_t dl_err; 490269Sericheng t_scalar_t sap; 491269Sericheng queue_t *q = dsp->ds_wq; 492269Sericheng 4931521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 4941521Syz147064 495269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 496269Sericheng dl_err = DL_BADPRIM; 497269Sericheng goto failed; 498269Sericheng } 499269Sericheng 500269Sericheng if (dlp->dl_xidtest_flg != 0) { 501269Sericheng dl_err = DL_NOAUTO; 502269Sericheng goto failed; 503269Sericheng } 504269Sericheng 505269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 506269Sericheng dl_err = DL_UNSUPPORTED; 507269Sericheng goto failed; 508269Sericheng } 509269Sericheng 510269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 511269Sericheng dl_err = DL_OUTSTATE; 512269Sericheng goto failed; 513269Sericheng } 5140Sstevel@tonic-gate 515269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 516269Sericheng !dls_active_set(dsp->ds_dc)) { 517269Sericheng dl_err = DL_SYSERR; 518269Sericheng err = EBUSY; 519269Sericheng goto failed; 520269Sericheng } 521269Sericheng 522269Sericheng dsp->ds_dlstate = DL_BIND_PENDING; 523269Sericheng /* 524269Sericheng * Set the receive callback. 525269Sericheng */ 526269Sericheng dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ? 527269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 5280Sstevel@tonic-gate 529269Sericheng /* 530269Sericheng * Bind the channel such that it can receive packets. 531269Sericheng */ 532269Sericheng sap = dsp->ds_sap = dlp->dl_sap; 533269Sericheng err = dls_bind(dsp->ds_dc, dlp->dl_sap); 534269Sericheng if (err != 0) { 535269Sericheng switch (err) { 536269Sericheng case EINVAL: 537269Sericheng dl_err = DL_BADADDR; 538269Sericheng err = 0; 539269Sericheng break; 540269Sericheng default: 541269Sericheng dl_err = DL_SYSERR; 542269Sericheng break; 543269Sericheng } 544269Sericheng dsp->ds_dlstate = DL_UNBOUND; 545269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 546269Sericheng dls_active_clear(dsp->ds_dc); 547269Sericheng 5480Sstevel@tonic-gate goto failed; 549269Sericheng } 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* 5520Sstevel@tonic-gate * Copy in MAC address. 5530Sstevel@tonic-gate */ 5543037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length; 5553037Syz147064 bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length); 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate /* 5583037Syz147064 * Copy in the SAP. 5590Sstevel@tonic-gate */ 5603037Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = dsp->ds_sap; 5613037Syz147064 dlsap_addr_length += sizeof (uint16_t); 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 564269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 565269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 5660Sstevel@tonic-gate 567269Sericheng rw_exit(&dsp->ds_lock); 568269Sericheng 5693037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); 570269Sericheng return (B_TRUE); 5710Sstevel@tonic-gate failed: 572269Sericheng rw_exit(&dsp->ds_lock); 573269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 574269Sericheng return (B_FALSE); 5750Sstevel@tonic-gate } 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate /* 578269Sericheng * DL_UNBIND_REQ 5790Sstevel@tonic-gate */ 580269Sericheng /*ARGSUSED*/ 5811353Sericheng static void 5821353Sericheng proto_process_unbind_req(void *arg) 5830Sstevel@tonic-gate { 5841353Sericheng dld_str_t *dsp = arg; 5851353Sericheng mblk_t *mp; 586269Sericheng 5871353Sericheng /* 5881353Sericheng * We don't need to hold locks because no other thread 5891353Sericheng * would manipulate dsp while it is in a PENDING state. 5901353Sericheng */ 5911353Sericheng ASSERT(dsp->ds_pending_req != NULL); 5921353Sericheng ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING); 593269Sericheng 594269Sericheng /* 595269Sericheng * Flush any remaining packets scheduled for transmission. 596269Sericheng */ 597269Sericheng dld_tx_flush(dsp); 598269Sericheng 599269Sericheng /* 600269Sericheng * Unbind the channel to stop packets being received. 601269Sericheng */ 602269Sericheng dls_unbind(dsp->ds_dc); 603269Sericheng 604269Sericheng /* 605269Sericheng * Disable polling mode, if it is enabled. 606269Sericheng */ 607269Sericheng proto_poll_disable(dsp); 608269Sericheng 609269Sericheng /* 610*3115Syl150051 * Clear LSO flags. 611*3115Syl150051 */ 612*3115Syl150051 dsp->ds_lso = B_FALSE; 613*3115Syl150051 dsp->ds_lso_max = 0; 614*3115Syl150051 615*3115Syl150051 /* 616269Sericheng * Clear the receive callback. 617269Sericheng */ 618269Sericheng dls_rx_set(dsp->ds_dc, NULL, NULL); 619269Sericheng 620269Sericheng /* 621269Sericheng * Set the mode back to the default (unitdata). 622269Sericheng */ 623269Sericheng dsp->ds_mode = DLD_UNITDATA; 624269Sericheng 6251184Skrgopi /* 6261184Skrgopi * If soft rings were enabled, the workers 6271353Sericheng * should be quiesced. We cannot check for 6281184Skrgopi * ds_soft_ring flag because 6291184Skrgopi * proto_soft_ring_disable() called from 6301184Skrgopi * proto_capability_req() would have reset it. 6311184Skrgopi */ 6321353Sericheng if (dls_soft_ring_workers(dsp->ds_dc)) 6331353Sericheng dls_soft_ring_disable(dsp->ds_dc); 6341353Sericheng 6351353Sericheng mp = dsp->ds_pending_req; 6361353Sericheng dsp->ds_pending_req = NULL; 6371353Sericheng dsp->ds_dlstate = DL_UNBOUND; 6381353Sericheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 6391353Sericheng 6401353Sericheng DLD_WAKEUP(dsp); 6411353Sericheng } 6421353Sericheng 6431353Sericheng /*ARGSUSED*/ 6441353Sericheng static boolean_t 6451353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6461353Sericheng { 6471353Sericheng queue_t *q = dsp->ds_wq; 6481353Sericheng t_uscalar_t dl_err; 6491353Sericheng 6501353Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 6511353Sericheng 6521353Sericheng if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 6531353Sericheng dl_err = DL_BADPRIM; 6541353Sericheng goto failed; 6551184Skrgopi } 6561184Skrgopi 6571353Sericheng if (dsp->ds_dlstate != DL_IDLE) { 6581353Sericheng dl_err = DL_OUTSTATE; 6591353Sericheng goto failed; 6601353Sericheng } 6611353Sericheng 6621353Sericheng dsp->ds_dlstate = DL_UNBIND_PENDING; 6631353Sericheng 6641353Sericheng mutex_enter(&dsp->ds_thr_lock); 6651353Sericheng ASSERT(dsp->ds_pending_req == NULL); 6661353Sericheng dsp->ds_pending_req = mp; 6671353Sericheng dsp->ds_pending_op = proto_process_unbind_req; 6681353Sericheng dsp->ds_pending_cnt++; 6691353Sericheng mutex_exit(&dsp->ds_thr_lock); 670269Sericheng rw_exit(&dsp->ds_lock); 671269Sericheng 672269Sericheng return (B_TRUE); 673269Sericheng failed: 674269Sericheng rw_exit(&dsp->ds_lock); 675269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 676269Sericheng return (B_FALSE); 6770Sstevel@tonic-gate } 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate /* 680269Sericheng * DL_PROMISCON_REQ 6810Sstevel@tonic-gate */ 682269Sericheng static boolean_t 683269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6840Sstevel@tonic-gate { 685269Sericheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp; 686269Sericheng int err = 0; 687269Sericheng t_uscalar_t dl_err; 688269Sericheng uint32_t promisc_saved; 689269Sericheng queue_t *q = dsp->ds_wq; 690269Sericheng 6911521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 6921521Syz147064 693269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 694269Sericheng dl_err = DL_BADPRIM; 695269Sericheng goto failed; 696269Sericheng } 697269Sericheng 698269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 699269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 700269Sericheng dl_err = DL_OUTSTATE; 7010Sstevel@tonic-gate goto failed; 702269Sericheng } 7030Sstevel@tonic-gate 704269Sericheng promisc_saved = dsp->ds_promisc; 705269Sericheng switch (dlp->dl_level) { 706269Sericheng case DL_PROMISC_SAP: 707269Sericheng dsp->ds_promisc |= DLS_PROMISC_SAP; 708269Sericheng break; 709269Sericheng 710269Sericheng case DL_PROMISC_MULTI: 711269Sericheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 712269Sericheng break; 713269Sericheng 714269Sericheng case DL_PROMISC_PHYS: 715269Sericheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 716269Sericheng break; 717269Sericheng 718269Sericheng default: 719269Sericheng dl_err = DL_NOTSUPPORTED; 720269Sericheng goto failed; 721269Sericheng } 7220Sstevel@tonic-gate 723269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 724269Sericheng !dls_active_set(dsp->ds_dc)) { 725269Sericheng dsp->ds_promisc = promisc_saved; 726269Sericheng dl_err = DL_SYSERR; 727269Sericheng err = EBUSY; 728269Sericheng goto failed; 729269Sericheng } 730269Sericheng 731269Sericheng /* 732269Sericheng * Adjust channel promiscuity. 733269Sericheng */ 734269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 735269Sericheng if (err != 0) { 736269Sericheng dl_err = DL_SYSERR; 737269Sericheng dsp->ds_promisc = promisc_saved; 738269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 739269Sericheng dls_active_clear(dsp->ds_dc); 740269Sericheng 741269Sericheng goto failed; 742269Sericheng } 743269Sericheng 744269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 745269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 746269Sericheng 747269Sericheng rw_exit(&dsp->ds_lock); 748269Sericheng dlokack(q, mp, DL_PROMISCON_REQ); 749269Sericheng return (B_TRUE); 7500Sstevel@tonic-gate failed: 751269Sericheng rw_exit(&dsp->ds_lock); 752269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 753269Sericheng return (B_FALSE); 7540Sstevel@tonic-gate } 7550Sstevel@tonic-gate 7560Sstevel@tonic-gate /* 757269Sericheng * DL_PROMISCOFF_REQ 7580Sstevel@tonic-gate */ 759269Sericheng static boolean_t 760269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 7610Sstevel@tonic-gate { 762269Sericheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp; 763269Sericheng int err = 0; 764269Sericheng t_uscalar_t dl_err; 765269Sericheng uint32_t promisc_saved; 766269Sericheng queue_t *q = dsp->ds_wq; 767269Sericheng 7681521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 769269Sericheng 770269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 771269Sericheng dl_err = DL_BADPRIM; 7720Sstevel@tonic-gate goto failed; 773269Sericheng } 7740Sstevel@tonic-gate 775269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 776269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 777269Sericheng dl_err = DL_OUTSTATE; 7780Sstevel@tonic-gate goto failed; 779269Sericheng } 7800Sstevel@tonic-gate 781269Sericheng promisc_saved = dsp->ds_promisc; 782269Sericheng switch (dlp->dl_level) { 783269Sericheng case DL_PROMISC_SAP: 784269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 785269Sericheng dl_err = DL_NOTENAB; 786269Sericheng goto failed; 787269Sericheng } 788269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 7890Sstevel@tonic-gate break; 7900Sstevel@tonic-gate 791269Sericheng case DL_PROMISC_MULTI: 792269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 793269Sericheng dl_err = DL_NOTENAB; 794269Sericheng goto failed; 795269Sericheng } 796269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 797269Sericheng break; 798269Sericheng 799269Sericheng case DL_PROMISC_PHYS: 800269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 801269Sericheng dl_err = DL_NOTENAB; 802269Sericheng goto failed; 803269Sericheng } 804269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 8050Sstevel@tonic-gate break; 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate default: 808269Sericheng dl_err = DL_NOTSUPPORTED; 809269Sericheng goto failed; 810269Sericheng } 811269Sericheng 812269Sericheng /* 813269Sericheng * Adjust channel promiscuity. 814269Sericheng */ 815269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 816269Sericheng if (err != 0) { 817269Sericheng dsp->ds_promisc = promisc_saved; 8180Sstevel@tonic-gate dl_err = DL_SYSERR; 819269Sericheng goto failed; 820269Sericheng } 821269Sericheng 822269Sericheng rw_exit(&dsp->ds_lock); 823269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 824269Sericheng return (B_TRUE); 825269Sericheng failed: 826269Sericheng rw_exit(&dsp->ds_lock); 827269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 828269Sericheng return (B_FALSE); 829269Sericheng } 830269Sericheng 831269Sericheng /* 832269Sericheng * DL_ENABMULTI_REQ 833269Sericheng */ 834269Sericheng static boolean_t 835269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 836269Sericheng { 837269Sericheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp; 838269Sericheng int err = 0; 839269Sericheng t_uscalar_t dl_err; 840269Sericheng queue_t *q = dsp->ds_wq; 841269Sericheng 842269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 843269Sericheng 844269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 845269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 846269Sericheng dl_err = DL_OUTSTATE; 847269Sericheng goto failed; 848269Sericheng } 849269Sericheng 850269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 851269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 852269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 853269Sericheng dl_err = DL_BADPRIM; 854269Sericheng goto failed; 855269Sericheng } 856269Sericheng 857269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 858269Sericheng !dls_active_set(dsp->ds_dc)) { 859269Sericheng dl_err = DL_SYSERR; 860269Sericheng err = EBUSY; 861269Sericheng goto failed; 8620Sstevel@tonic-gate } 8630Sstevel@tonic-gate 864269Sericheng err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 865269Sericheng if (err != 0) { 866269Sericheng switch (err) { 867269Sericheng case EINVAL: 868269Sericheng dl_err = DL_BADADDR; 869269Sericheng err = 0; 870269Sericheng break; 871269Sericheng case ENOSPC: 872269Sericheng dl_err = DL_TOOMANY; 873269Sericheng err = 0; 874269Sericheng break; 875269Sericheng default: 876269Sericheng dl_err = DL_SYSERR; 877269Sericheng break; 878269Sericheng } 879269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 880269Sericheng dls_active_clear(dsp->ds_dc); 881269Sericheng 882269Sericheng goto failed; 883269Sericheng } 884269Sericheng 885269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 886269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 887269Sericheng 888269Sericheng rw_exit(&dsp->ds_lock); 889269Sericheng dlokack(q, mp, DL_ENABMULTI_REQ); 890269Sericheng return (B_TRUE); 891269Sericheng failed: 892269Sericheng rw_exit(&dsp->ds_lock); 893269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 894269Sericheng return (B_FALSE); 895269Sericheng } 896269Sericheng 897269Sericheng /* 898269Sericheng * DL_DISABMULTI_REQ 899269Sericheng */ 900269Sericheng static boolean_t 901269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 902269Sericheng { 903269Sericheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp; 904269Sericheng int err = 0; 905269Sericheng t_uscalar_t dl_err; 906269Sericheng queue_t *q = dsp->ds_wq; 907269Sericheng 908269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 909269Sericheng 910269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 911269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 912269Sericheng dl_err = DL_OUTSTATE; 913269Sericheng goto failed; 914269Sericheng } 915269Sericheng 916269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 917269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 918269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 919269Sericheng dl_err = DL_BADPRIM; 920269Sericheng goto failed; 921269Sericheng } 922269Sericheng 923269Sericheng err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 924269Sericheng if (err != 0) { 925269Sericheng switch (err) { 926269Sericheng case EINVAL: 927269Sericheng dl_err = DL_BADADDR; 928269Sericheng err = 0; 929269Sericheng break; 930269Sericheng 931269Sericheng case ENOENT: 932269Sericheng dl_err = DL_NOTENAB; 933269Sericheng err = 0; 934269Sericheng break; 935269Sericheng 936269Sericheng default: 937269Sericheng dl_err = DL_SYSERR; 938269Sericheng break; 939269Sericheng } 940269Sericheng goto failed; 941269Sericheng } 942269Sericheng 943269Sericheng rw_exit(&dsp->ds_lock); 944269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 945269Sericheng return (B_TRUE); 946269Sericheng failed: 947269Sericheng rw_exit(&dsp->ds_lock); 948269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 949269Sericheng return (B_FALSE); 9500Sstevel@tonic-gate } 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate /* 953269Sericheng * DL_PHYS_ADDR_REQ 9540Sstevel@tonic-gate */ 955269Sericheng static boolean_t 956269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 9570Sstevel@tonic-gate { 958269Sericheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp; 959269Sericheng queue_t *q = dsp->ds_wq; 960269Sericheng t_uscalar_t dl_err; 961269Sericheng char *addr; 962269Sericheng uint_t addr_length; 963269Sericheng 964269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 965269Sericheng 966269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 967269Sericheng dl_err = DL_BADPRIM; 968269Sericheng goto failed; 969269Sericheng } 970269Sericheng 971269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 972269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 973269Sericheng dl_err = DL_OUTSTATE; 974269Sericheng goto failed; 975269Sericheng } 9760Sstevel@tonic-gate 977269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 978269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 979269Sericheng dl_err = DL_UNSUPPORTED; 9800Sstevel@tonic-gate goto failed; 981269Sericheng } 9820Sstevel@tonic-gate 983269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 984269Sericheng addr = kmem_alloc(addr_length, KM_NOSLEEP); 985269Sericheng if (addr == NULL) { 986269Sericheng rw_exit(&dsp->ds_lock); 987269Sericheng merror(q, mp, ENOSR); 988269Sericheng return (B_FALSE); 989269Sericheng } 9900Sstevel@tonic-gate 991269Sericheng /* 992269Sericheng * Copy out the address before we drop the lock; we don't 993269Sericheng * want to call dlphysaddrack() while holding ds_lock. 994269Sericheng */ 995269Sericheng bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ? 996269Sericheng dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length); 997269Sericheng 998269Sericheng rw_exit(&dsp->ds_lock); 999269Sericheng dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 1000269Sericheng kmem_free(addr, addr_length); 1001269Sericheng return (B_TRUE); 10020Sstevel@tonic-gate failed: 1003269Sericheng rw_exit(&dsp->ds_lock); 1004269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 1005269Sericheng return (B_FALSE); 1006269Sericheng } 10070Sstevel@tonic-gate 1008269Sericheng /* 1009269Sericheng * DL_SET_PHYS_ADDR_REQ 1010269Sericheng */ 1011269Sericheng static boolean_t 1012269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1013269Sericheng { 1014269Sericheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp; 1015269Sericheng int err = 0; 1016269Sericheng t_uscalar_t dl_err; 1017269Sericheng queue_t *q = dsp->ds_wq; 10180Sstevel@tonic-gate 1019269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1020269Sericheng 1021269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1022269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1023269Sericheng dl_err = DL_OUTSTATE; 1024269Sericheng goto failed; 1025269Sericheng } 1026269Sericheng 1027269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 1028269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 1029269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 1030269Sericheng dl_err = DL_BADPRIM; 1031269Sericheng goto failed; 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate 1034269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 1035269Sericheng !dls_active_set(dsp->ds_dc)) { 1036269Sericheng dl_err = DL_SYSERR; 1037269Sericheng err = EBUSY; 1038269Sericheng goto failed; 1039269Sericheng } 1040269Sericheng 1041269Sericheng err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); 1042269Sericheng if (err != 0) { 1043269Sericheng switch (err) { 1044269Sericheng case EINVAL: 1045269Sericheng dl_err = DL_BADADDR; 1046269Sericheng err = 0; 1047269Sericheng break; 1048269Sericheng 1049269Sericheng default: 1050269Sericheng dl_err = DL_SYSERR; 1051269Sericheng break; 1052269Sericheng } 1053269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1054269Sericheng dls_active_clear(dsp->ds_dc); 1055269Sericheng 1056269Sericheng goto failed; 1057269Sericheng } 1058269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1059269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 1060269Sericheng 1061269Sericheng rw_exit(&dsp->ds_lock); 1062269Sericheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 1063269Sericheng return (B_TRUE); 1064269Sericheng failed: 1065269Sericheng rw_exit(&dsp->ds_lock); 1066269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 1067269Sericheng return (B_FALSE); 1068269Sericheng } 1069269Sericheng 1070269Sericheng /* 1071269Sericheng * DL_UDQOS_REQ 1072269Sericheng */ 1073269Sericheng static boolean_t 1074269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1075269Sericheng { 1076269Sericheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp; 1077269Sericheng dl_qos_cl_sel1_t *selp; 1078269Sericheng int off, len; 1079269Sericheng t_uscalar_t dl_err; 1080269Sericheng queue_t *q = dsp->ds_wq; 1081269Sericheng 1082269Sericheng off = dlp->dl_qos_offset; 1083269Sericheng len = dlp->dl_qos_length; 1084269Sericheng 10851521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 10861521Syz147064 1087269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 1088269Sericheng dl_err = DL_BADPRIM; 1089269Sericheng goto failed; 1090269Sericheng } 1091269Sericheng 1092269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 1093269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 1094269Sericheng dl_err = DL_BADQOSTYPE; 1095269Sericheng goto failed; 1096269Sericheng } 1097269Sericheng 10982760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 1099269Sericheng selp->dl_priority < 0) { 1100269Sericheng dl_err = DL_BADQOSPARAM; 1101269Sericheng goto failed; 1102269Sericheng } 1103269Sericheng 1104269Sericheng dsp->ds_pri = selp->dl_priority; 1105269Sericheng 1106269Sericheng rw_exit(&dsp->ds_lock); 1107269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 1108269Sericheng return (B_TRUE); 1109269Sericheng failed: 1110269Sericheng rw_exit(&dsp->ds_lock); 1111269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 1112269Sericheng return (B_FALSE); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate 11151184Skrgopi static boolean_t 11161184Skrgopi check_ip_above(queue_t *q) 11171184Skrgopi { 11181184Skrgopi queue_t *next_q; 11191184Skrgopi boolean_t ret = B_TRUE; 11201184Skrgopi 11211184Skrgopi claimstr(q); 11221184Skrgopi next_q = q->q_next; 11231184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 11241184Skrgopi ret = B_FALSE; 11251184Skrgopi releasestr(q); 11261184Skrgopi return (ret); 11271184Skrgopi } 11281184Skrgopi 11290Sstevel@tonic-gate /* 1130269Sericheng * DL_CAPABILITY_REQ 11310Sstevel@tonic-gate */ 1132269Sericheng /*ARGSUSED*/ 1133269Sericheng static boolean_t 1134269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 11350Sstevel@tonic-gate { 1136269Sericheng dl_capability_req_t *dlp = (dl_capability_req_t *)udlp; 1137269Sericheng dl_capability_sub_t *sp; 1138269Sericheng size_t size, len; 1139269Sericheng offset_t off, end; 1140269Sericheng t_uscalar_t dl_err; 1141269Sericheng queue_t *q = dsp->ds_wq; 1142269Sericheng boolean_t upgraded; 1143269Sericheng 1144269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1145269Sericheng 1146269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1147269Sericheng dl_err = DL_BADPRIM; 1148269Sericheng goto failed; 1149269Sericheng } 1150269Sericheng 1151269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1152269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1153269Sericheng dl_err = DL_OUTSTATE; 1154269Sericheng goto failed; 1155269Sericheng } 1156269Sericheng 1157269Sericheng /* 1158269Sericheng * This request is overloaded. If there are no requested capabilities 1159269Sericheng * then we just want to acknowledge with all the capabilities we 1160269Sericheng * support. Otherwise we enable the set of capabilities requested. 1161269Sericheng */ 1162269Sericheng if (dlp->dl_sub_length == 0) { 1163269Sericheng /* callee drops lock */ 1164269Sericheng return (proto_capability_advertise(dsp, mp)); 1165269Sericheng } 1166269Sericheng 1167269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1168269Sericheng dl_err = DL_BADPRIM; 1169269Sericheng goto failed; 1170269Sericheng } 1171269Sericheng 1172269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1173269Sericheng 1174269Sericheng off = dlp->dl_sub_offset; 1175269Sericheng len = dlp->dl_sub_length; 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate /* 1178269Sericheng * Walk the list of capabilities to be enabled. 11790Sstevel@tonic-gate */ 1180269Sericheng upgraded = B_FALSE; 1181269Sericheng for (end = off + len; off < end; ) { 1182269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1183269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1184269Sericheng 1185269Sericheng if (off + size > end || 1186269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1187269Sericheng dl_err = DL_BADPRIM; 1188269Sericheng goto failed; 1189269Sericheng } 1190269Sericheng 1191269Sericheng switch (sp->dl_cap) { 1192269Sericheng /* 1193269Sericheng * TCP/IP checksum offload to hardware. 1194269Sericheng */ 1195269Sericheng case DL_CAPAB_HCKSUM: { 1196269Sericheng dl_capab_hcksum_t *hcksump; 1197269Sericheng dl_capab_hcksum_t hcksum; 1198269Sericheng 1199269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1200269Sericheng /* 1201269Sericheng * Copy for alignment. 1202269Sericheng */ 1203269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1204269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1205269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1206269Sericheng break; 1207269Sericheng } 1208269Sericheng 1209269Sericheng /* 1210*3115Syl150051 * Large segment offload. (LSO) 1211*3115Syl150051 */ 1212*3115Syl150051 case DL_CAPAB_LSO: { 1213*3115Syl150051 dl_capab_lso_t *lsop; 1214*3115Syl150051 dl_capab_lso_t lso; 1215*3115Syl150051 1216*3115Syl150051 lsop = (dl_capab_lso_t *)&sp[1]; 1217*3115Syl150051 /* 1218*3115Syl150051 * Copy for alignment. 1219*3115Syl150051 */ 1220*3115Syl150051 bcopy(lsop, &lso, sizeof (dl_capab_lso_t)); 1221*3115Syl150051 dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq); 1222*3115Syl150051 bcopy(&lso, lsop, sizeof (dl_capab_lso_t)); 1223*3115Syl150051 break; 1224*3115Syl150051 } 1225*3115Syl150051 1226*3115Syl150051 /* 1227269Sericheng * IP polling interface. 1228269Sericheng */ 1229269Sericheng case DL_CAPAB_POLL: { 12301184Skrgopi dl_capab_dls_t *pollp; 12311184Skrgopi dl_capab_dls_t poll; 1232269Sericheng 12331184Skrgopi pollp = (dl_capab_dls_t *)&sp[1]; 1234269Sericheng /* 1235269Sericheng * Copy for alignment. 1236269Sericheng */ 12371184Skrgopi bcopy(pollp, &poll, sizeof (dl_capab_dls_t)); 1238269Sericheng 1239269Sericheng /* 1240269Sericheng * We need to become writer before enabling and/or 1241269Sericheng * disabling the polling interface. If we couldn' 1242269Sericheng * upgrade, check state again after re-acquiring the 1243269Sericheng * lock to make sure we can proceed. 1244269Sericheng */ 1245269Sericheng if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 1246269Sericheng rw_exit(&dsp->ds_lock); 1247269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1248269Sericheng 1249269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1250269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1251269Sericheng dl_err = DL_OUTSTATE; 1252269Sericheng goto failed; 1253269Sericheng } 1254269Sericheng } 1255269Sericheng upgraded = B_TRUE; 1256269Sericheng 12571184Skrgopi switch (poll.dls_flags) { 1258269Sericheng default: 1259269Sericheng /*FALLTHRU*/ 1260269Sericheng case POLL_DISABLE: 1261269Sericheng proto_poll_disable(dsp); 1262269Sericheng break; 1263269Sericheng 1264269Sericheng case POLL_ENABLE: 1265269Sericheng ASSERT(!(dld_opt & DLD_OPT_NO_POLL)); 1266269Sericheng 1267269Sericheng /* 1268269Sericheng * Make sure polling is disabled. 1269269Sericheng */ 1270269Sericheng proto_poll_disable(dsp); 1271269Sericheng 1272269Sericheng /* 1273269Sericheng * Now attempt enable it. 1274269Sericheng */ 12751184Skrgopi if (check_ip_above(dsp->ds_rq) && 12761184Skrgopi proto_poll_enable(dsp, &poll)) { 12771184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 12781184Skrgopi poll.dls_flags = POLL_ENABLE; 12791184Skrgopi } 1280269Sericheng break; 1281269Sericheng } 1282269Sericheng 12831184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 12841184Skrgopi bcopy(&poll, pollp, sizeof (dl_capab_dls_t)); 12851184Skrgopi break; 12861184Skrgopi } 12871184Skrgopi case DL_CAPAB_SOFT_RING: { 12881184Skrgopi dl_capab_dls_t *soft_ringp; 12891184Skrgopi dl_capab_dls_t soft_ring; 12901184Skrgopi 12911184Skrgopi soft_ringp = (dl_capab_dls_t *)&sp[1]; 12921184Skrgopi /* 12931184Skrgopi * Copy for alignment. 12941184Skrgopi */ 12951184Skrgopi bcopy(soft_ringp, &soft_ring, 12961184Skrgopi sizeof (dl_capab_dls_t)); 12971184Skrgopi 12981184Skrgopi /* 12991184Skrgopi * We need to become writer before enabling and/or 13001184Skrgopi * disabling the soft_ring interface. If we couldn' 13011184Skrgopi * upgrade, check state again after re-acquiring the 13021184Skrgopi * lock to make sure we can proceed. 13031184Skrgopi */ 13041184Skrgopi if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 13051184Skrgopi rw_exit(&dsp->ds_lock); 13061184Skrgopi rw_enter(&dsp->ds_lock, RW_WRITER); 13071184Skrgopi 13081184Skrgopi if (dsp->ds_dlstate == DL_UNATTACHED || 13091184Skrgopi DL_ACK_PENDING(dsp->ds_dlstate)) { 13101184Skrgopi dl_err = DL_OUTSTATE; 13111184Skrgopi goto failed; 13121184Skrgopi } 13131184Skrgopi } 13141184Skrgopi upgraded = B_TRUE; 13151184Skrgopi 13161184Skrgopi switch (soft_ring.dls_flags) { 13171184Skrgopi default: 13181184Skrgopi /*FALLTHRU*/ 13191184Skrgopi case SOFT_RING_DISABLE: 13201184Skrgopi proto_soft_ring_disable(dsp); 13211184Skrgopi break; 13221184Skrgopi 13231184Skrgopi case SOFT_RING_ENABLE: 13241184Skrgopi /* 13251184Skrgopi * Make sure soft_ring is disabled. 13261184Skrgopi */ 13271184Skrgopi proto_soft_ring_disable(dsp); 13281184Skrgopi 13291184Skrgopi /* 13301184Skrgopi * Now attempt enable it. 13311184Skrgopi */ 13321184Skrgopi if (check_ip_above(dsp->ds_rq) && 13331184Skrgopi proto_soft_ring_enable(dsp, &soft_ring)) { 13341184Skrgopi bzero(&soft_ring, 13351184Skrgopi sizeof (dl_capab_dls_t)); 13361184Skrgopi soft_ring.dls_flags = 13371184Skrgopi SOFT_RING_ENABLE; 13381184Skrgopi } else { 13391184Skrgopi bzero(&soft_ring, 13401184Skrgopi sizeof (dl_capab_dls_t)); 13411184Skrgopi soft_ring.dls_flags = 13421184Skrgopi SOFT_RING_DISABLE; 13431184Skrgopi } 13441184Skrgopi break; 13451184Skrgopi } 13461184Skrgopi 13471184Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 13481184Skrgopi bcopy(&soft_ring, soft_ringp, 13491184Skrgopi sizeof (dl_capab_dls_t)); 1350269Sericheng break; 1351269Sericheng } 1352269Sericheng default: 1353269Sericheng break; 1354269Sericheng } 1355269Sericheng 1356269Sericheng off += size; 1357269Sericheng } 1358269Sericheng rw_exit(&dsp->ds_lock); 1359269Sericheng qreply(q, mp); 1360269Sericheng return (B_TRUE); 1361269Sericheng failed: 1362269Sericheng rw_exit(&dsp->ds_lock); 1363269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 1364269Sericheng return (B_FALSE); 13650Sstevel@tonic-gate } 13660Sstevel@tonic-gate 13670Sstevel@tonic-gate /* 1368269Sericheng * DL_NOTIFY_REQ 13690Sstevel@tonic-gate */ 1370269Sericheng static boolean_t 1371269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 13720Sstevel@tonic-gate { 1373269Sericheng dl_notify_req_t *dlp = (dl_notify_req_t *)udlp; 1374269Sericheng t_uscalar_t dl_err; 1375269Sericheng queue_t *q = dsp->ds_wq; 1376269Sericheng uint_t note = 1377269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1378269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1379269Sericheng DL_NOTE_PHYS_ADDR | 1380269Sericheng DL_NOTE_LINK_UP | 1381269Sericheng DL_NOTE_LINK_DOWN | 13822311Sseb DL_NOTE_CAPAB_RENEG | 13832311Sseb DL_NOTE_SPEED; 13840Sstevel@tonic-gate 13851521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 13861521Syz147064 1387269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1388269Sericheng dl_err = DL_BADPRIM; 1389269Sericheng goto failed; 1390269Sericheng } 13910Sstevel@tonic-gate 1392269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1393269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1394269Sericheng dl_err = DL_OUTSTATE; 1395269Sericheng goto failed; 13960Sstevel@tonic-gate } 13970Sstevel@tonic-gate 1398269Sericheng /* 1399269Sericheng * Cache the notifications that are being enabled. 1400269Sericheng */ 1401269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1402269Sericheng rw_exit(&dsp->ds_lock); 1403269Sericheng /* 1404269Sericheng * The ACK carries all notifications regardless of which set is 1405269Sericheng * being enabled. 1406269Sericheng */ 1407269Sericheng dlnotifyack(q, mp, note); 1408269Sericheng 1409269Sericheng /* 1410269Sericheng * Solicit DL_NOTIFY_IND messages for each enabled notification. 1411269Sericheng */ 1412269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1413269Sericheng if (dsp->ds_notifications != 0) { 1414269Sericheng rw_exit(&dsp->ds_lock); 1415269Sericheng dld_str_notify_ind(dsp); 1416269Sericheng } else { 1417269Sericheng rw_exit(&dsp->ds_lock); 1418269Sericheng } 1419269Sericheng return (B_TRUE); 1420269Sericheng failed: 1421269Sericheng rw_exit(&dsp->ds_lock); 1422269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 1423269Sericheng return (B_FALSE); 14240Sstevel@tonic-gate } 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate /* 1427269Sericheng * DL_UINTDATA_REQ 14280Sstevel@tonic-gate */ 1429269Sericheng static boolean_t 1430269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 14310Sstevel@tonic-gate { 1432269Sericheng queue_t *q = dsp->ds_wq; 1433269Sericheng dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)udlp; 1434269Sericheng off_t off; 1435269Sericheng size_t len, size; 1436269Sericheng const uint8_t *addr; 1437269Sericheng uint16_t sap; 1438269Sericheng uint_t addr_length; 14392311Sseb mblk_t *bp, *payload; 1440269Sericheng uint32_t start, stuff, end, value, flags; 1441269Sericheng t_uscalar_t dl_err; 1442269Sericheng 1443269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1444269Sericheng 1445269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1446269Sericheng dl_err = DL_BADPRIM; 1447269Sericheng goto failed; 1448269Sericheng } 1449269Sericheng 1450269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 1451269Sericheng dl_err = DL_OUTSTATE; 1452269Sericheng goto failed; 1453269Sericheng } 1454269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1455269Sericheng 1456269Sericheng off = dlp->dl_dest_addr_offset; 1457269Sericheng len = dlp->dl_dest_addr_length; 1458269Sericheng 1459269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1460269Sericheng dl_err = DL_BADPRIM; 1461269Sericheng goto failed; 1462269Sericheng } 1463269Sericheng 1464269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1465269Sericheng dl_err = DL_BADADDR; 1466269Sericheng goto failed; 1467269Sericheng } 1468269Sericheng 1469269Sericheng addr = mp->b_rptr + off; 1470269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1471269Sericheng 1472269Sericheng /* 1473269Sericheng * Check the length of the packet and the block types. 1474269Sericheng */ 1475269Sericheng size = 0; 14762311Sseb payload = mp->b_cont; 14772311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1478269Sericheng if (DB_TYPE(bp) != M_DATA) 1479269Sericheng goto baddata; 1480269Sericheng 1481269Sericheng size += MBLKL(bp); 1482269Sericheng } 1483269Sericheng 1484269Sericheng if (size > dsp->ds_mip->mi_sdu_max) 1485269Sericheng goto baddata; 1486269Sericheng 1487269Sericheng /* 1488269Sericheng * Build a packet header. 1489269Sericheng */ 14902760Sdg199075 if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max, 14912760Sdg199075 &payload)) == NULL) { 1492269Sericheng dl_err = DL_BADADDR; 1493269Sericheng goto failed; 1494269Sericheng } 1495269Sericheng 1496269Sericheng /* 1497269Sericheng * We no longer need the M_PROTO header, so free it. 1498269Sericheng */ 1499269Sericheng freeb(mp); 1500269Sericheng 1501269Sericheng /* 1502269Sericheng * Transfer the checksum offload information if it is present. 1503269Sericheng */ 15042311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1505269Sericheng &flags); 15062311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1507269Sericheng 1508269Sericheng /* 1509269Sericheng * Link the payload onto the new header. 1510269Sericheng */ 1511269Sericheng ASSERT(bp->b_cont == NULL); 15122311Sseb bp->b_cont = payload; 1513269Sericheng 15142760Sdg199075 dld_tx_single(dsp, bp); 1515269Sericheng rw_exit(&dsp->ds_lock); 1516269Sericheng return (B_TRUE); 1517269Sericheng failed: 1518269Sericheng rw_exit(&dsp->ds_lock); 1519269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1520269Sericheng return (B_FALSE); 1521269Sericheng 1522269Sericheng baddata: 1523269Sericheng rw_exit(&dsp->ds_lock); 1524269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1525269Sericheng return (B_FALSE); 1526269Sericheng } 1527269Sericheng 1528269Sericheng /* 1529269Sericheng * DL_PASSIVE_REQ 1530269Sericheng */ 1531269Sericheng /* ARGSUSED */ 1532269Sericheng static boolean_t 1533269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1534269Sericheng { 1535269Sericheng t_uscalar_t dl_err; 1536269Sericheng 1537269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1538269Sericheng /* 1539269Sericheng * If we've already become active by issuing an active primitive, 1540269Sericheng * then it's too late to try to become passive. 1541269Sericheng */ 1542269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1543269Sericheng dl_err = DL_OUTSTATE; 1544269Sericheng goto failed; 1545269Sericheng } 1546269Sericheng 1547269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1548269Sericheng dl_err = DL_BADPRIM; 1549269Sericheng goto failed; 1550269Sericheng } 1551269Sericheng 1552269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1553269Sericheng rw_exit(&dsp->ds_lock); 1554269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1555269Sericheng return (B_TRUE); 1556269Sericheng failed: 1557269Sericheng rw_exit(&dsp->ds_lock); 1558269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1559269Sericheng return (B_FALSE); 1560269Sericheng } 1561269Sericheng 1562269Sericheng 1563269Sericheng /* 1564269Sericheng * Catch-all handler. 1565269Sericheng */ 1566269Sericheng static boolean_t 1567269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp) 1568269Sericheng { 1569269Sericheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1570269Sericheng return (B_FALSE); 15710Sstevel@tonic-gate } 15720Sstevel@tonic-gate 15730Sstevel@tonic-gate static void 15740Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp) 15750Sstevel@tonic-gate { 15760Sstevel@tonic-gate mac_handle_t mh; 15770Sstevel@tonic-gate 15781353Sericheng ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock)); 1579269Sericheng 15800Sstevel@tonic-gate if (!dsp->ds_polling) 15810Sstevel@tonic-gate return; 15820Sstevel@tonic-gate 15830Sstevel@tonic-gate /* 15840Sstevel@tonic-gate * It should be impossible to enable raw mode if polling is turned on. 15850Sstevel@tonic-gate */ 15860Sstevel@tonic-gate ASSERT(dsp->ds_mode != DLD_RAW); 15870Sstevel@tonic-gate 15880Sstevel@tonic-gate /* 15890Sstevel@tonic-gate * Reset the resource_add callback. 15900Sstevel@tonic-gate */ 15910Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 15920Sstevel@tonic-gate mac_resource_set(mh, NULL, NULL); 15931184Skrgopi mac_resources(mh); 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate /* 15960Sstevel@tonic-gate * Set receive function back to default. 15970Sstevel@tonic-gate */ 15980Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ? 15990Sstevel@tonic-gate dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 16000Sstevel@tonic-gate 16010Sstevel@tonic-gate /* 16020Sstevel@tonic-gate * Note that polling is disabled. 16030Sstevel@tonic-gate */ 16040Sstevel@tonic-gate dsp->ds_polling = B_FALSE; 16050Sstevel@tonic-gate } 16060Sstevel@tonic-gate 16070Sstevel@tonic-gate static boolean_t 16081184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp) 16090Sstevel@tonic-gate { 16100Sstevel@tonic-gate mac_handle_t mh; 16110Sstevel@tonic-gate 1612269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16130Sstevel@tonic-gate ASSERT(!dsp->ds_polling); 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate /* 16160Sstevel@tonic-gate * We cannot enable polling if raw mode 16170Sstevel@tonic-gate * has been enabled. 16180Sstevel@tonic-gate */ 16190Sstevel@tonic-gate if (dsp->ds_mode == DLD_RAW) 16200Sstevel@tonic-gate return (B_FALSE); 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate /* 16250Sstevel@tonic-gate * Register resources. 16260Sstevel@tonic-gate */ 16271184Skrgopi mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add, 16281184Skrgopi (void *)pollp->dls_rx_handle); 16290Sstevel@tonic-gate mac_resources(mh); 16300Sstevel@tonic-gate 16310Sstevel@tonic-gate /* 16320Sstevel@tonic-gate * Set the receive function. 16330Sstevel@tonic-gate */ 16341184Skrgopi dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx, 16351184Skrgopi (void *)pollp->dls_rx_handle); 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate /* 16380Sstevel@tonic-gate * Note that polling is enabled. This prevents further DLIOCHDRINFO 16390Sstevel@tonic-gate * ioctls from overwriting the receive function pointer. 16400Sstevel@tonic-gate */ 16410Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 16420Sstevel@tonic-gate return (B_TRUE); 16430Sstevel@tonic-gate } 16440Sstevel@tonic-gate 16451184Skrgopi static void 16461184Skrgopi proto_soft_ring_disable(dld_str_t *dsp) 16471184Skrgopi { 16481184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16491184Skrgopi 16501184Skrgopi if (!dsp->ds_soft_ring) 16511184Skrgopi return; 16521184Skrgopi 16531184Skrgopi /* 16541184Skrgopi * It should be impossible to enable raw mode if soft_ring is turned on. 16551184Skrgopi */ 16561184Skrgopi ASSERT(dsp->ds_mode != DLD_RAW); 16571184Skrgopi proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE); 16581184Skrgopi /* 16591184Skrgopi * Note that fanout is disabled. 16601184Skrgopi */ 16611184Skrgopi dsp->ds_soft_ring = B_FALSE; 16621184Skrgopi } 16631184Skrgopi 16641184Skrgopi static boolean_t 16651184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp) 16661184Skrgopi { 16671184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16681184Skrgopi ASSERT(!dsp->ds_soft_ring); 16691184Skrgopi 16701184Skrgopi /* 16711184Skrgopi * We cannot enable soft_ring if raw mode 16721184Skrgopi * has been enabled. 16731184Skrgopi */ 16741184Skrgopi if (dsp->ds_mode == DLD_RAW) 16751184Skrgopi return (B_FALSE); 16761184Skrgopi 16771184Skrgopi if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE) 16781184Skrgopi return (B_FALSE); 16791184Skrgopi 16801184Skrgopi dsp->ds_soft_ring = B_TRUE; 16811184Skrgopi return (B_TRUE); 16821184Skrgopi } 16831184Skrgopi 16841184Skrgopi static void 16851184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type) 16861184Skrgopi { 16871184Skrgopi dls_rx_t rx; 16881184Skrgopi 16891184Skrgopi if (type == SOFT_RING_NONE) { 16901184Skrgopi rx = (dsp->ds_mode == DLD_FASTPATH) ? 16911184Skrgopi dld_str_rx_fastpath : dld_str_rx_unitdata; 16921184Skrgopi } else { 16931184Skrgopi rx = (dls_rx_t)dls_ether_soft_ring_fanout; 16941184Skrgopi } 16951184Skrgopi dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type); 16961184Skrgopi } 16971184Skrgopi 16980Sstevel@tonic-gate /* 16990Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 17000Sstevel@tonic-gate */ 1701269Sericheng static boolean_t 1702269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 17030Sstevel@tonic-gate { 17040Sstevel@tonic-gate dl_capability_ack_t *dlap; 17050Sstevel@tonic-gate dl_capability_sub_t *dlsp; 17060Sstevel@tonic-gate size_t subsize; 17071184Skrgopi dl_capab_dls_t poll; 1708*3115Syl150051 dl_capab_dls_t soft_ring; 17090Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 1710*3115Syl150051 dl_capab_lso_t lso; 17110Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 17120Sstevel@tonic-gate uint8_t *ptr; 17132311Sseb boolean_t cksum_cap; 17140Sstevel@tonic-gate boolean_t poll_cap; 1715*3115Syl150051 boolean_t lso_cap; 1716*3115Syl150051 mac_capab_lso_t mac_lso; 1717269Sericheng queue_t *q = dsp->ds_wq; 1718269Sericheng mblk_t *mp1; 1719269Sericheng 1720269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 17210Sstevel@tonic-gate 17220Sstevel@tonic-gate /* 17230Sstevel@tonic-gate * Initially assume no capabilities. 17240Sstevel@tonic-gate */ 17250Sstevel@tonic-gate subsize = 0; 17260Sstevel@tonic-gate 17271555Skrgopi /* 17281555Skrgopi * Advertize soft ring capability if 17291555Skrgopi * VLAN_ID_NONE for GLDv3 drivers 17301555Skrgopi */ 17311555Skrgopi if (dsp->ds_vid == VLAN_ID_NONE) 17321555Skrgopi subsize += sizeof (dl_capability_sub_t) + 17331555Skrgopi sizeof (dl_capab_dls_t); 17341184Skrgopi 17350Sstevel@tonic-gate /* 17360Sstevel@tonic-gate * Check if polling can be enabled on this interface. 17370Sstevel@tonic-gate * If advertising DL_CAPAB_POLL has not been explicitly disabled 17380Sstevel@tonic-gate * then reserve space for that capability. 17390Sstevel@tonic-gate */ 17402311Sseb poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) && 17410Sstevel@tonic-gate !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE)); 17420Sstevel@tonic-gate if (poll_cap) { 17430Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17441184Skrgopi sizeof (dl_capab_dls_t); 17450Sstevel@tonic-gate } 17460Sstevel@tonic-gate 17470Sstevel@tonic-gate /* 17480Sstevel@tonic-gate * If the MAC interface supports checksum offload then reserve 17490Sstevel@tonic-gate * space for the DL_CAPAB_HCKSUM capability. 17500Sstevel@tonic-gate */ 17512311Sseb if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 17522311Sseb &hcksum.hcksum_txflags)) { 17530Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17540Sstevel@tonic-gate sizeof (dl_capab_hcksum_t); 17550Sstevel@tonic-gate } 17560Sstevel@tonic-gate 17570Sstevel@tonic-gate /* 1758*3115Syl150051 * If LSO is usable for MAC, reserve space for the DL_CAPAB_LSO 1759*3115Syl150051 * capability. 1760*3115Syl150051 */ 1761*3115Syl150051 if (lso_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { 1762*3115Syl150051 subsize += sizeof (dl_capability_sub_t) + 1763*3115Syl150051 sizeof (dl_capab_lso_t); 1764*3115Syl150051 } 1765*3115Syl150051 1766*3115Syl150051 /* 17670Sstevel@tonic-gate * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then 17680Sstevel@tonic-gate * reserve space for it. 17690Sstevel@tonic-gate */ 17700Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 17710Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17720Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 17730Sstevel@tonic-gate } 17740Sstevel@tonic-gate 17750Sstevel@tonic-gate /* 1776269Sericheng * If there are no capabilities to advertise or if we 1777269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 17780Sstevel@tonic-gate */ 17791184Skrgopi if ((mp1 = reallocb(mp, 1780269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1781269Sericheng rw_exit(&dsp->ds_lock); 1782269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1783269Sericheng return (B_FALSE); 17840Sstevel@tonic-gate } 17850Sstevel@tonic-gate 1786269Sericheng mp = mp1; 1787269Sericheng DB_TYPE(mp) = M_PROTO; 1788269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1789269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 17900Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 17910Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 17920Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 17930Sstevel@tonic-gate dlap->dl_sub_length = subsize; 17940Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 17950Sstevel@tonic-gate 17960Sstevel@tonic-gate /* 17970Sstevel@tonic-gate * IP polling interface. 17980Sstevel@tonic-gate */ 17990Sstevel@tonic-gate if (poll_cap) { 18000Sstevel@tonic-gate /* 1801269Sericheng * Attempt to disable just in case this is a re-negotiation; 1802269Sericheng * we need to become writer before doing so. 18030Sstevel@tonic-gate */ 1804269Sericheng if (!rw_tryupgrade(&dsp->ds_lock)) { 1805269Sericheng rw_exit(&dsp->ds_lock); 1806269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1807269Sericheng } 18080Sstevel@tonic-gate 1809269Sericheng /* 1810269Sericheng * Check if polling state has changed after we re-acquired 1811269Sericheng * the lock above, so that we don't mis-advertise it. 1812269Sericheng */ 18132311Sseb poll_cap = !(dld_opt & DLD_OPT_NO_POLL) && 18142311Sseb (dsp->ds_vid == VLAN_ID_NONE); 18150Sstevel@tonic-gate 1816269Sericheng if (!poll_cap) { 1817269Sericheng int poll_capab_size; 1818269Sericheng 1819269Sericheng rw_downgrade(&dsp->ds_lock); 1820269Sericheng 1821269Sericheng poll_capab_size = sizeof (dl_capability_sub_t) + 18221184Skrgopi sizeof (dl_capab_dls_t); 18230Sstevel@tonic-gate 1824269Sericheng mp->b_wptr -= poll_capab_size; 1825269Sericheng subsize -= poll_capab_size; 1826269Sericheng dlap->dl_sub_length = subsize; 1827269Sericheng } else { 1828269Sericheng proto_poll_disable(dsp); 1829269Sericheng 1830269Sericheng rw_downgrade(&dsp->ds_lock); 1831269Sericheng 1832269Sericheng dlsp = (dl_capability_sub_t *)ptr; 1833269Sericheng 1834269Sericheng dlsp->dl_cap = DL_CAPAB_POLL; 18351184Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 1836269Sericheng ptr += sizeof (dl_capability_sub_t); 18370Sstevel@tonic-gate 18381184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 18391184Skrgopi poll.dls_version = POLL_VERSION_1; 18401184Skrgopi poll.dls_flags = POLL_CAPABLE; 18411184Skrgopi poll.dls_tx_handle = (uintptr_t)dsp; 18421184Skrgopi poll.dls_tx = (uintptr_t)str_mdata_fastpath_put; 1843269Sericheng 18441184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 18451184Skrgopi bcopy(&poll, ptr, sizeof (dl_capab_dls_t)); 18461184Skrgopi ptr += sizeof (dl_capab_dls_t); 1847269Sericheng } 18480Sstevel@tonic-gate } 18490Sstevel@tonic-gate 1850269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 1851269Sericheng 18521555Skrgopi if (dsp->ds_vid == VLAN_ID_NONE) { 18531555Skrgopi dlsp = (dl_capability_sub_t *)ptr; 18541184Skrgopi 18551555Skrgopi dlsp->dl_cap = DL_CAPAB_SOFT_RING; 18561555Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 18571555Skrgopi ptr += sizeof (dl_capability_sub_t); 18581184Skrgopi 18591555Skrgopi bzero(&soft_ring, sizeof (dl_capab_dls_t)); 18601555Skrgopi soft_ring.dls_version = SOFT_RING_VERSION_1; 18611555Skrgopi soft_ring.dls_flags = SOFT_RING_CAPABLE; 18621555Skrgopi soft_ring.dls_tx_handle = (uintptr_t)dsp; 18631555Skrgopi soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put; 18641555Skrgopi soft_ring.dls_ring_change_status = 18651555Skrgopi (uintptr_t)proto_change_soft_ring_fanout; 18661555Skrgopi soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind; 18671555Skrgopi soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind; 18681184Skrgopi 18691555Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 18701555Skrgopi bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t)); 18711555Skrgopi ptr += sizeof (dl_capab_dls_t); 18721555Skrgopi } 18731184Skrgopi 18740Sstevel@tonic-gate /* 18750Sstevel@tonic-gate * TCP/IP checksum offload. 18760Sstevel@tonic-gate */ 18772311Sseb if (cksum_cap) { 18780Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18790Sstevel@tonic-gate 18800Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 18810Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 18820Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 18850Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 18860Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 18870Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 18880Sstevel@tonic-gate } 18890Sstevel@tonic-gate 18900Sstevel@tonic-gate /* 1891*3115Syl150051 * Large segment offload. (LSO) 1892*3115Syl150051 */ 1893*3115Syl150051 if (lso_cap) { 1894*3115Syl150051 dlsp = (dl_capability_sub_t *)ptr; 1895*3115Syl150051 1896*3115Syl150051 dlsp->dl_cap = DL_CAPAB_LSO; 1897*3115Syl150051 dlsp->dl_length = sizeof (dl_capab_lso_t); 1898*3115Syl150051 ptr += sizeof (dl_capability_sub_t); 1899*3115Syl150051 1900*3115Syl150051 lso.lso_version = LSO_VERSION_1; 1901*3115Syl150051 lso.lso_flags = mac_lso.lso_flags; 1902*3115Syl150051 lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; 1903*3115Syl150051 1904*3115Syl150051 /* Simply enable LSO with DLD */ 1905*3115Syl150051 dsp->ds_lso = B_TRUE; 1906*3115Syl150051 dsp->ds_lso_max = lso.lso_max; 1907*3115Syl150051 1908*3115Syl150051 dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq); 1909*3115Syl150051 bcopy(&lso, ptr, sizeof (dl_capab_lso_t)); 1910*3115Syl150051 ptr += sizeof (dl_capab_lso_t); 1911*3115Syl150051 } else { 1912*3115Syl150051 dsp->ds_lso = B_FALSE; 1913*3115Syl150051 dsp->ds_lso_max = 0; 1914*3115Syl150051 } 1915*3115Syl150051 1916*3115Syl150051 /* 19170Sstevel@tonic-gate * Zero copy 19180Sstevel@tonic-gate */ 19190Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 19200Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 19210Sstevel@tonic-gate 19220Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 19230Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 19240Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 19250Sstevel@tonic-gate 19260Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 19270Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 19280Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 19290Sstevel@tonic-gate 19300Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 19310Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 19320Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 19330Sstevel@tonic-gate } 19340Sstevel@tonic-gate 19350Sstevel@tonic-gate ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1936269Sericheng 1937269Sericheng rw_exit(&dsp->ds_lock); 1938269Sericheng qreply(q, mp); 1939269Sericheng return (B_TRUE); 19400Sstevel@tonic-gate } 1941