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 /* 223844Skrgopi * Copyright 2007 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 */ 2773147Sxc151355 if (dsp->ds_native) 2783147Sxc151355 dlp->dl_mac_type = minfop->mi_nativemedia; 2793147Sxc151355 else 2803147Sxc151355 dlp->dl_mac_type = minfop->mi_media; 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate /* 2830Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2840Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2850Sstevel@tonic-gate */ 2860Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2870Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate /* 2900Sstevel@tonic-gate * Set the minimum and maximum payload sizes. 2910Sstevel@tonic-gate */ 292269Sericheng dlp->dl_min_sdu = minfop->mi_sdu_min; 293269Sericheng dlp->dl_max_sdu = minfop->mi_sdu_max; 2940Sstevel@tonic-gate 295269Sericheng addr_length = minfop->mi_addr_length; 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate /* 2980Sstevel@tonic-gate * Copy in the media broadcast address. 2990Sstevel@tonic-gate */ 3002311Sseb if (minfop->mi_brdcst_addr != NULL) { 3012311Sseb dlp->dl_brdcst_addr_offset = 3022311Sseb (uintptr_t)brdcst_addr - (uintptr_t)dlp; 3032311Sseb bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 3042311Sseb dlp->dl_brdcst_addr_length = addr_length; 3052311Sseb } 3060Sstevel@tonic-gate 3072760Sdg199075 dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 3082760Sdg199075 dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 3090Sstevel@tonic-gate 3102760Sdg199075 rangep->dl_qos_type = DL_QOS_CL_RANGE1; 3112760Sdg199075 rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 3122760Sdg199075 rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 3132760Sdg199075 rangep->dl_protection.dl_min = DL_UNKNOWN; 3142760Sdg199075 rangep->dl_protection.dl_max = DL_UNKNOWN; 3152760Sdg199075 rangep->dl_residual_error = DL_UNKNOWN; 3160Sstevel@tonic-gate 3172760Sdg199075 /* 3182760Sdg199075 * Specify the supported range of priorities. 3192760Sdg199075 */ 3202760Sdg199075 rangep->dl_priority.dl_min = 0; 3212760Sdg199075 rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 3220Sstevel@tonic-gate 3232760Sdg199075 dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 3242760Sdg199075 dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 3250Sstevel@tonic-gate 3262760Sdg199075 selp->dl_qos_type = DL_QOS_CL_SEL1; 3272760Sdg199075 selp->dl_trans_delay = DL_UNKNOWN; 3282760Sdg199075 selp->dl_protection = DL_UNKNOWN; 3292760Sdg199075 selp->dl_residual_error = DL_UNKNOWN; 3302760Sdg199075 3312760Sdg199075 /* 3322760Sdg199075 * Specify the current priority (which can be changed by 3332760Sdg199075 * the DL_UDQOS_REQ primitive). 3342760Sdg199075 */ 3352760Sdg199075 selp->dl_priority = dsp->ds_pri; 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 3380Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 3390Sstevel@tonic-gate /* 3400Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 3410Sstevel@tonic-gate * DLSAP address. 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 3442311Sseb if (addr_length > 0) 3452311Sseb bcopy(dsp->ds_curr_addr, addr, addr_length); 3460Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate done: 3500Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3510Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3520Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3530Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3540Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3550Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3560Sstevel@tonic-gate 357269Sericheng rw_exit(&dsp->ds_lock); 358269Sericheng 359269Sericheng qreply(q, mp); 360269Sericheng return (B_TRUE); 361269Sericheng } 362269Sericheng 363269Sericheng /* 364269Sericheng * DL_ATTACH_REQ 365269Sericheng */ 366269Sericheng static boolean_t 367269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 368269Sericheng { 369269Sericheng dl_attach_req_t *dlp = (dl_attach_req_t *)udlp; 370269Sericheng int err = 0; 371269Sericheng t_uscalar_t dl_err; 372269Sericheng queue_t *q = dsp->ds_wq; 373269Sericheng 374269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 375269Sericheng 376269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 377269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 378269Sericheng dl_err = DL_BADPRIM; 379269Sericheng goto failed; 380269Sericheng } 381269Sericheng 382269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 383269Sericheng dl_err = DL_OUTSTATE; 384269Sericheng goto failed; 385269Sericheng } 386269Sericheng 387269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 388269Sericheng 389269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 390269Sericheng if (err != 0) { 391269Sericheng switch (err) { 392269Sericheng case ENOENT: 393269Sericheng dl_err = DL_BADPPA; 394269Sericheng err = 0; 395269Sericheng break; 396269Sericheng default: 397269Sericheng dl_err = DL_SYSERR; 398269Sericheng break; 399269Sericheng } 400269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 401269Sericheng goto failed; 402269Sericheng } 403269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 404269Sericheng rw_exit(&dsp->ds_lock); 405269Sericheng 406269Sericheng dlokack(q, mp, DL_ATTACH_REQ); 407269Sericheng return (B_TRUE); 408269Sericheng failed: 409269Sericheng rw_exit(&dsp->ds_lock); 410269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 411269Sericheng return (B_FALSE); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate /* 415269Sericheng * DL_DETACH_REQ 4160Sstevel@tonic-gate */ 4171353Sericheng static void 4181353Sericheng proto_process_detach_req(void *arg) 4191353Sericheng { 4201353Sericheng dld_str_t *dsp = arg; 4211353Sericheng mblk_t *mp; 4221353Sericheng 4231353Sericheng /* 4241353Sericheng * We don't need to hold locks because no other thread 4251353Sericheng * would manipulate dsp while it is in a PENDING state. 4261353Sericheng */ 4271353Sericheng ASSERT(dsp->ds_pending_req != NULL); 4281353Sericheng ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING); 4291353Sericheng 4301353Sericheng mp = dsp->ds_pending_req; 4311353Sericheng dsp->ds_pending_req = NULL; 4321353Sericheng dld_str_detach(dsp); 4331353Sericheng dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 4341353Sericheng 4351353Sericheng DLD_WAKEUP(dsp); 4361353Sericheng } 4371353Sericheng 438269Sericheng /*ARGSUSED*/ 439269Sericheng static boolean_t 440269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4410Sstevel@tonic-gate { 442269Sericheng queue_t *q = dsp->ds_wq; 443269Sericheng t_uscalar_t dl_err; 4440Sstevel@tonic-gate 445269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 4460Sstevel@tonic-gate 447269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 448269Sericheng dl_err = DL_BADPRIM; 449269Sericheng goto failed; 450269Sericheng } 4510Sstevel@tonic-gate 452269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 453269Sericheng dl_err = DL_OUTSTATE; 454269Sericheng goto failed; 4550Sstevel@tonic-gate } 4560Sstevel@tonic-gate 457269Sericheng if (dsp->ds_style == DL_STYLE1) { 458269Sericheng dl_err = DL_BADPRIM; 459269Sericheng goto failed; 460269Sericheng } 461269Sericheng 462269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 463269Sericheng 464269Sericheng /* 465269Sericheng * Complete the detach when the driver is single-threaded. 466269Sericheng */ 467269Sericheng mutex_enter(&dsp->ds_thr_lock); 4681353Sericheng ASSERT(dsp->ds_pending_req == NULL); 4691353Sericheng dsp->ds_pending_req = mp; 4701353Sericheng dsp->ds_pending_op = proto_process_detach_req; 4711353Sericheng dsp->ds_pending_cnt++; 472269Sericheng mutex_exit(&dsp->ds_thr_lock); 473269Sericheng rw_exit(&dsp->ds_lock); 474269Sericheng 475269Sericheng return (B_TRUE); 476269Sericheng failed: 477269Sericheng rw_exit(&dsp->ds_lock); 478269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 479269Sericheng return (B_FALSE); 4800Sstevel@tonic-gate } 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate /* 483269Sericheng * DL_BIND_REQ 4840Sstevel@tonic-gate */ 485269Sericheng static boolean_t 486269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4870Sstevel@tonic-gate { 488269Sericheng dl_bind_req_t *dlp = (dl_bind_req_t *)udlp; 489269Sericheng int err = 0; 4903037Syz147064 uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; 4913037Syz147064 uint_t dlsap_addr_length; 492269Sericheng t_uscalar_t dl_err; 493269Sericheng t_scalar_t sap; 494269Sericheng queue_t *q = dsp->ds_wq; 495269Sericheng 4961521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 4971521Syz147064 498269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 499269Sericheng dl_err = DL_BADPRIM; 500269Sericheng goto failed; 501269Sericheng } 502269Sericheng 503269Sericheng if (dlp->dl_xidtest_flg != 0) { 504269Sericheng dl_err = DL_NOAUTO; 505269Sericheng goto failed; 506269Sericheng } 507269Sericheng 508269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 509269Sericheng dl_err = DL_UNSUPPORTED; 510269Sericheng goto failed; 511269Sericheng } 512269Sericheng 513269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 514269Sericheng dl_err = DL_OUTSTATE; 515269Sericheng goto failed; 516269Sericheng } 5170Sstevel@tonic-gate 518269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 519269Sericheng !dls_active_set(dsp->ds_dc)) { 520269Sericheng dl_err = DL_SYSERR; 521269Sericheng err = EBUSY; 522269Sericheng goto failed; 523269Sericheng } 524269Sericheng 525269Sericheng dsp->ds_dlstate = DL_BIND_PENDING; 526269Sericheng /* 527269Sericheng * Set the receive callback. 528269Sericheng */ 529269Sericheng dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ? 530269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 5310Sstevel@tonic-gate 532269Sericheng /* 533269Sericheng * Bind the channel such that it can receive packets. 534269Sericheng */ 535269Sericheng sap = dsp->ds_sap = dlp->dl_sap; 536269Sericheng err = dls_bind(dsp->ds_dc, dlp->dl_sap); 537269Sericheng if (err != 0) { 538269Sericheng switch (err) { 539269Sericheng case EINVAL: 540269Sericheng dl_err = DL_BADADDR; 541269Sericheng err = 0; 542269Sericheng break; 543269Sericheng default: 544269Sericheng dl_err = DL_SYSERR; 545269Sericheng break; 546269Sericheng } 547269Sericheng dsp->ds_dlstate = DL_UNBOUND; 548269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 549269Sericheng dls_active_clear(dsp->ds_dc); 550269Sericheng 5510Sstevel@tonic-gate goto failed; 552269Sericheng } 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate /* 5550Sstevel@tonic-gate * Copy in MAC address. 5560Sstevel@tonic-gate */ 5573037Syz147064 dlsap_addr_length = dsp->ds_mip->mi_addr_length; 5583037Syz147064 bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length); 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 5613037Syz147064 * Copy in the SAP. 5620Sstevel@tonic-gate */ 5633037Syz147064 *(uint16_t *)(dlsap_addr + dlsap_addr_length) = dsp->ds_sap; 5643037Syz147064 dlsap_addr_length += sizeof (uint16_t); 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 567269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 568269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 5690Sstevel@tonic-gate 570269Sericheng rw_exit(&dsp->ds_lock); 571269Sericheng 5723037Syz147064 dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); 573269Sericheng return (B_TRUE); 5740Sstevel@tonic-gate failed: 575269Sericheng rw_exit(&dsp->ds_lock); 576269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 577269Sericheng return (B_FALSE); 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate /* 581269Sericheng * DL_UNBIND_REQ 5820Sstevel@tonic-gate */ 583269Sericheng /*ARGSUSED*/ 5841353Sericheng static void 5851353Sericheng proto_process_unbind_req(void *arg) 5860Sstevel@tonic-gate { 5871353Sericheng dld_str_t *dsp = arg; 5881353Sericheng mblk_t *mp; 589269Sericheng 5901353Sericheng /* 5911353Sericheng * We don't need to hold locks because no other thread 5921353Sericheng * would manipulate dsp while it is in a PENDING state. 5931353Sericheng */ 5941353Sericheng ASSERT(dsp->ds_pending_req != NULL); 5951353Sericheng ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING); 596269Sericheng 597269Sericheng /* 598269Sericheng * Flush any remaining packets scheduled for transmission. 599269Sericheng */ 600269Sericheng dld_tx_flush(dsp); 601269Sericheng 602269Sericheng /* 603269Sericheng * Unbind the channel to stop packets being received. 604269Sericheng */ 605269Sericheng dls_unbind(dsp->ds_dc); 606269Sericheng 607269Sericheng /* 608269Sericheng * Disable polling mode, if it is enabled. 609269Sericheng */ 610269Sericheng proto_poll_disable(dsp); 611269Sericheng 612269Sericheng /* 6133115Syl150051 * Clear LSO flags. 6143115Syl150051 */ 6153115Syl150051 dsp->ds_lso = B_FALSE; 6163115Syl150051 dsp->ds_lso_max = 0; 6173115Syl150051 6183115Syl150051 /* 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 6301353Sericheng * 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 */ 6351353Sericheng if (dls_soft_ring_workers(dsp->ds_dc)) 6361353Sericheng dls_soft_ring_disable(dsp->ds_dc); 6371353Sericheng 6381353Sericheng mp = dsp->ds_pending_req; 6391353Sericheng dsp->ds_pending_req = NULL; 6401353Sericheng dsp->ds_dlstate = DL_UNBOUND; 6411353Sericheng dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); 6421353Sericheng 6431353Sericheng DLD_WAKEUP(dsp); 6441353Sericheng } 6451353Sericheng 6461353Sericheng /*ARGSUSED*/ 6471353Sericheng static boolean_t 6481353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6491353Sericheng { 6501353Sericheng queue_t *q = dsp->ds_wq; 6511353Sericheng t_uscalar_t dl_err; 6521353Sericheng 6531353Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 6541353Sericheng 6551353Sericheng if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 6561353Sericheng dl_err = DL_BADPRIM; 6571353Sericheng goto failed; 6581184Skrgopi } 6591184Skrgopi 6601353Sericheng if (dsp->ds_dlstate != DL_IDLE) { 6611353Sericheng dl_err = DL_OUTSTATE; 6621353Sericheng goto failed; 6631353Sericheng } 6641353Sericheng 6651353Sericheng dsp->ds_dlstate = DL_UNBIND_PENDING; 6661353Sericheng 6671353Sericheng mutex_enter(&dsp->ds_thr_lock); 6681353Sericheng ASSERT(dsp->ds_pending_req == NULL); 6691353Sericheng dsp->ds_pending_req = mp; 6701353Sericheng dsp->ds_pending_op = proto_process_unbind_req; 6711353Sericheng dsp->ds_pending_cnt++; 6721353Sericheng 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 6941521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 6951521Syz147064 696269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 697269Sericheng dl_err = DL_BADPRIM; 698269Sericheng goto failed; 699269Sericheng } 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 7711521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 772269Sericheng 773269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 774269Sericheng dl_err = DL_BADPRIM; 7750Sstevel@tonic-gate goto failed; 776269Sericheng } 7770Sstevel@tonic-gate 778269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 779269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 780269Sericheng dl_err = DL_OUTSTATE; 7810Sstevel@tonic-gate goto failed; 782269Sericheng } 7830Sstevel@tonic-gate 784269Sericheng promisc_saved = dsp->ds_promisc; 785269Sericheng switch (dlp->dl_level) { 786269Sericheng case DL_PROMISC_SAP: 787269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 788269Sericheng dl_err = DL_NOTENAB; 789269Sericheng goto failed; 790269Sericheng } 791269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 7920Sstevel@tonic-gate break; 7930Sstevel@tonic-gate 794269Sericheng case DL_PROMISC_MULTI: 795269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 796269Sericheng dl_err = DL_NOTENAB; 797269Sericheng goto failed; 798269Sericheng } 799269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 800269Sericheng break; 801269Sericheng 802269Sericheng case DL_PROMISC_PHYS: 803269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 804269Sericheng dl_err = DL_NOTENAB; 805269Sericheng goto failed; 806269Sericheng } 807269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 8080Sstevel@tonic-gate break; 8090Sstevel@tonic-gate 8100Sstevel@tonic-gate default: 811269Sericheng dl_err = DL_NOTSUPPORTED; 812269Sericheng goto failed; 813269Sericheng } 814269Sericheng 815269Sericheng /* 816269Sericheng * Adjust channel promiscuity. 817269Sericheng */ 818269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 819269Sericheng if (err != 0) { 820269Sericheng dsp->ds_promisc = promisc_saved; 8210Sstevel@tonic-gate dl_err = DL_SYSERR; 822269Sericheng goto failed; 823269Sericheng } 824269Sericheng 825269Sericheng rw_exit(&dsp->ds_lock); 826269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 827269Sericheng return (B_TRUE); 828269Sericheng failed: 829269Sericheng rw_exit(&dsp->ds_lock); 830269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 831269Sericheng return (B_FALSE); 832269Sericheng } 833269Sericheng 834269Sericheng /* 835269Sericheng * DL_ENABMULTI_REQ 836269Sericheng */ 837269Sericheng static boolean_t 838269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 839269Sericheng { 840269Sericheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp; 841269Sericheng int err = 0; 842269Sericheng t_uscalar_t dl_err; 843269Sericheng queue_t *q = dsp->ds_wq; 844269Sericheng 845269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 846269Sericheng 847269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 848269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 849269Sericheng dl_err = DL_OUTSTATE; 850269Sericheng goto failed; 851269Sericheng } 852269Sericheng 853269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 854269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 855269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 856269Sericheng dl_err = DL_BADPRIM; 857269Sericheng goto failed; 858269Sericheng } 859269Sericheng 860269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 861269Sericheng !dls_active_set(dsp->ds_dc)) { 862269Sericheng dl_err = DL_SYSERR; 863269Sericheng err = EBUSY; 864269Sericheng goto failed; 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate 867269Sericheng err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 868269Sericheng if (err != 0) { 869269Sericheng switch (err) { 870269Sericheng case EINVAL: 871269Sericheng dl_err = DL_BADADDR; 872269Sericheng err = 0; 873269Sericheng break; 874269Sericheng case ENOSPC: 875269Sericheng dl_err = DL_TOOMANY; 876269Sericheng err = 0; 877269Sericheng break; 878269Sericheng default: 879269Sericheng dl_err = DL_SYSERR; 880269Sericheng break; 881269Sericheng } 882269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 883269Sericheng dls_active_clear(dsp->ds_dc); 884269Sericheng 885269Sericheng goto failed; 886269Sericheng } 887269Sericheng 888269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 889269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 890269Sericheng 891269Sericheng rw_exit(&dsp->ds_lock); 892269Sericheng dlokack(q, mp, DL_ENABMULTI_REQ); 893269Sericheng return (B_TRUE); 894269Sericheng failed: 895269Sericheng rw_exit(&dsp->ds_lock); 896269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 897269Sericheng return (B_FALSE); 898269Sericheng } 899269Sericheng 900269Sericheng /* 901269Sericheng * DL_DISABMULTI_REQ 902269Sericheng */ 903269Sericheng static boolean_t 904269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 905269Sericheng { 906269Sericheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp; 907269Sericheng int err = 0; 908269Sericheng t_uscalar_t dl_err; 909269Sericheng queue_t *q = dsp->ds_wq; 910269Sericheng 911269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 912269Sericheng 913269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 914269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 915269Sericheng dl_err = DL_OUTSTATE; 916269Sericheng goto failed; 917269Sericheng } 918269Sericheng 919269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 920269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 921269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 922269Sericheng dl_err = DL_BADPRIM; 923269Sericheng goto failed; 924269Sericheng } 925269Sericheng 926269Sericheng err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 927269Sericheng if (err != 0) { 928269Sericheng switch (err) { 929269Sericheng case EINVAL: 930269Sericheng dl_err = DL_BADADDR; 931269Sericheng err = 0; 932269Sericheng break; 933269Sericheng 934269Sericheng case ENOENT: 935269Sericheng dl_err = DL_NOTENAB; 936269Sericheng err = 0; 937269Sericheng break; 938269Sericheng 939269Sericheng default: 940269Sericheng dl_err = DL_SYSERR; 941269Sericheng break; 942269Sericheng } 943269Sericheng goto failed; 944269Sericheng } 945269Sericheng 946269Sericheng rw_exit(&dsp->ds_lock); 947269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 948269Sericheng return (B_TRUE); 949269Sericheng failed: 950269Sericheng rw_exit(&dsp->ds_lock); 951269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 952269Sericheng return (B_FALSE); 9530Sstevel@tonic-gate } 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate /* 956269Sericheng * DL_PHYS_ADDR_REQ 9570Sstevel@tonic-gate */ 958269Sericheng static boolean_t 959269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 9600Sstevel@tonic-gate { 961269Sericheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp; 962269Sericheng queue_t *q = dsp->ds_wq; 963269Sericheng t_uscalar_t dl_err; 964269Sericheng char *addr; 965269Sericheng uint_t addr_length; 966269Sericheng 967269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 968269Sericheng 969269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 970269Sericheng dl_err = DL_BADPRIM; 971269Sericheng goto failed; 972269Sericheng } 973269Sericheng 974269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 975269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 976269Sericheng dl_err = DL_OUTSTATE; 977269Sericheng goto failed; 978269Sericheng } 9790Sstevel@tonic-gate 980269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 981269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 982269Sericheng dl_err = DL_UNSUPPORTED; 9830Sstevel@tonic-gate goto failed; 984269Sericheng } 9850Sstevel@tonic-gate 986269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 987269Sericheng addr = kmem_alloc(addr_length, KM_NOSLEEP); 988269Sericheng if (addr == NULL) { 989269Sericheng rw_exit(&dsp->ds_lock); 990269Sericheng merror(q, mp, ENOSR); 991269Sericheng return (B_FALSE); 992269Sericheng } 9930Sstevel@tonic-gate 994269Sericheng /* 995269Sericheng * Copy out the address before we drop the lock; we don't 996269Sericheng * want to call dlphysaddrack() while holding ds_lock. 997269Sericheng */ 998269Sericheng bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ? 999269Sericheng dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length); 1000269Sericheng 1001269Sericheng rw_exit(&dsp->ds_lock); 1002269Sericheng dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 1003269Sericheng kmem_free(addr, addr_length); 1004269Sericheng return (B_TRUE); 10050Sstevel@tonic-gate failed: 1006269Sericheng rw_exit(&dsp->ds_lock); 1007269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 1008269Sericheng return (B_FALSE); 1009269Sericheng } 10100Sstevel@tonic-gate 1011269Sericheng /* 1012269Sericheng * DL_SET_PHYS_ADDR_REQ 1013269Sericheng */ 1014269Sericheng static boolean_t 1015269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1016269Sericheng { 1017269Sericheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp; 1018269Sericheng int err = 0; 1019269Sericheng t_uscalar_t dl_err; 1020269Sericheng queue_t *q = dsp->ds_wq; 10210Sstevel@tonic-gate 1022269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1023269Sericheng 1024269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1025269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1026269Sericheng dl_err = DL_OUTSTATE; 1027269Sericheng goto failed; 1028269Sericheng } 1029269Sericheng 1030269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 1031269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 1032269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 1033269Sericheng dl_err = DL_BADPRIM; 1034269Sericheng goto failed; 10350Sstevel@tonic-gate } 10360Sstevel@tonic-gate 1037269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 1038269Sericheng !dls_active_set(dsp->ds_dc)) { 1039269Sericheng dl_err = DL_SYSERR; 1040269Sericheng err = EBUSY; 1041269Sericheng goto failed; 1042269Sericheng } 1043269Sericheng 1044269Sericheng err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); 1045269Sericheng if (err != 0) { 1046269Sericheng switch (err) { 1047269Sericheng case EINVAL: 1048269Sericheng dl_err = DL_BADADDR; 1049269Sericheng err = 0; 1050269Sericheng break; 1051269Sericheng 1052269Sericheng default: 1053269Sericheng dl_err = DL_SYSERR; 1054269Sericheng break; 1055269Sericheng } 1056269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1057269Sericheng dls_active_clear(dsp->ds_dc); 1058269Sericheng 1059269Sericheng goto failed; 1060269Sericheng } 1061269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1062269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 1063269Sericheng 1064269Sericheng rw_exit(&dsp->ds_lock); 1065269Sericheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 1066269Sericheng return (B_TRUE); 1067269Sericheng failed: 1068269Sericheng rw_exit(&dsp->ds_lock); 1069269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 1070269Sericheng return (B_FALSE); 1071269Sericheng } 1072269Sericheng 1073269Sericheng /* 1074269Sericheng * DL_UDQOS_REQ 1075269Sericheng */ 1076269Sericheng static boolean_t 1077269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1078269Sericheng { 1079269Sericheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp; 1080269Sericheng dl_qos_cl_sel1_t *selp; 1081269Sericheng int off, len; 1082269Sericheng t_uscalar_t dl_err; 1083269Sericheng queue_t *q = dsp->ds_wq; 1084269Sericheng 1085269Sericheng off = dlp->dl_qos_offset; 1086269Sericheng len = dlp->dl_qos_length; 1087269Sericheng 10881521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 10891521Syz147064 1090269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 1091269Sericheng dl_err = DL_BADPRIM; 1092269Sericheng goto failed; 1093269Sericheng } 1094269Sericheng 1095269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 1096269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 1097269Sericheng dl_err = DL_BADQOSTYPE; 1098269Sericheng goto failed; 1099269Sericheng } 1100269Sericheng 11012760Sdg199075 if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 1102269Sericheng selp->dl_priority < 0) { 1103269Sericheng dl_err = DL_BADQOSPARAM; 1104269Sericheng goto failed; 1105269Sericheng } 1106269Sericheng 1107269Sericheng dsp->ds_pri = selp->dl_priority; 1108269Sericheng 1109269Sericheng rw_exit(&dsp->ds_lock); 1110269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 1111269Sericheng return (B_TRUE); 1112269Sericheng failed: 1113269Sericheng rw_exit(&dsp->ds_lock); 1114269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 1115269Sericheng return (B_FALSE); 11160Sstevel@tonic-gate } 11170Sstevel@tonic-gate 11181184Skrgopi static boolean_t 11191184Skrgopi check_ip_above(queue_t *q) 11201184Skrgopi { 11211184Skrgopi queue_t *next_q; 11221184Skrgopi boolean_t ret = B_TRUE; 11231184Skrgopi 11241184Skrgopi claimstr(q); 11251184Skrgopi next_q = q->q_next; 11261184Skrgopi if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 11271184Skrgopi ret = B_FALSE; 11281184Skrgopi releasestr(q); 11291184Skrgopi return (ret); 11301184Skrgopi } 11311184Skrgopi 11320Sstevel@tonic-gate /* 1133269Sericheng * DL_CAPABILITY_REQ 11340Sstevel@tonic-gate */ 1135269Sericheng /*ARGSUSED*/ 1136269Sericheng static boolean_t 1137269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 11380Sstevel@tonic-gate { 1139269Sericheng dl_capability_req_t *dlp = (dl_capability_req_t *)udlp; 1140269Sericheng dl_capability_sub_t *sp; 1141269Sericheng size_t size, len; 1142269Sericheng offset_t off, end; 1143269Sericheng t_uscalar_t dl_err; 1144269Sericheng queue_t *q = dsp->ds_wq; 1145269Sericheng boolean_t upgraded; 1146269Sericheng 1147269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1148269Sericheng 1149269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1150269Sericheng dl_err = DL_BADPRIM; 1151269Sericheng goto failed; 1152269Sericheng } 1153269Sericheng 1154269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1155269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1156269Sericheng dl_err = DL_OUTSTATE; 1157269Sericheng goto failed; 1158269Sericheng } 1159269Sericheng 1160269Sericheng /* 1161269Sericheng * This request is overloaded. If there are no requested capabilities 1162269Sericheng * then we just want to acknowledge with all the capabilities we 1163269Sericheng * support. Otherwise we enable the set of capabilities requested. 1164269Sericheng */ 1165269Sericheng if (dlp->dl_sub_length == 0) { 1166269Sericheng /* callee drops lock */ 1167269Sericheng return (proto_capability_advertise(dsp, mp)); 1168269Sericheng } 1169269Sericheng 1170269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1171269Sericheng dl_err = DL_BADPRIM; 1172269Sericheng goto failed; 1173269Sericheng } 1174269Sericheng 1175269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1176269Sericheng 1177269Sericheng off = dlp->dl_sub_offset; 1178269Sericheng len = dlp->dl_sub_length; 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate /* 1181269Sericheng * Walk the list of capabilities to be enabled. 11820Sstevel@tonic-gate */ 1183269Sericheng upgraded = B_FALSE; 1184269Sericheng for (end = off + len; off < end; ) { 1185269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1186269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1187269Sericheng 1188269Sericheng if (off + size > end || 1189269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1190269Sericheng dl_err = DL_BADPRIM; 1191269Sericheng goto failed; 1192269Sericheng } 1193269Sericheng 1194269Sericheng switch (sp->dl_cap) { 1195269Sericheng /* 1196269Sericheng * TCP/IP checksum offload to hardware. 1197269Sericheng */ 1198269Sericheng case DL_CAPAB_HCKSUM: { 1199269Sericheng dl_capab_hcksum_t *hcksump; 1200269Sericheng dl_capab_hcksum_t hcksum; 1201269Sericheng 1202269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1203269Sericheng /* 1204269Sericheng * Copy for alignment. 1205269Sericheng */ 1206269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1207269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1208269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1209269Sericheng break; 1210269Sericheng } 1211269Sericheng 1212269Sericheng /* 12133115Syl150051 * Large segment offload. (LSO) 12143115Syl150051 */ 12153115Syl150051 case DL_CAPAB_LSO: { 12163115Syl150051 dl_capab_lso_t *lsop; 12173115Syl150051 dl_capab_lso_t lso; 12183115Syl150051 12193115Syl150051 lsop = (dl_capab_lso_t *)&sp[1]; 12203115Syl150051 /* 12213115Syl150051 * Copy for alignment. 12223115Syl150051 */ 12233115Syl150051 bcopy(lsop, &lso, sizeof (dl_capab_lso_t)); 12243115Syl150051 dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq); 12253115Syl150051 bcopy(&lso, lsop, sizeof (dl_capab_lso_t)); 12263115Syl150051 break; 12273115Syl150051 } 12283115Syl150051 12293115Syl150051 /* 1230269Sericheng * IP polling interface. 1231269Sericheng */ 1232269Sericheng case DL_CAPAB_POLL: { 12331184Skrgopi dl_capab_dls_t *pollp; 12341184Skrgopi dl_capab_dls_t poll; 1235269Sericheng 12361184Skrgopi pollp = (dl_capab_dls_t *)&sp[1]; 1237269Sericheng /* 1238269Sericheng * Copy for alignment. 1239269Sericheng */ 12401184Skrgopi bcopy(pollp, &poll, sizeof (dl_capab_dls_t)); 1241269Sericheng 1242269Sericheng /* 1243269Sericheng * We need to become writer before enabling and/or 1244269Sericheng * disabling the polling interface. If we couldn' 1245269Sericheng * upgrade, check state again after re-acquiring the 1246269Sericheng * lock to make sure we can proceed. 1247269Sericheng */ 1248269Sericheng if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 1249269Sericheng rw_exit(&dsp->ds_lock); 1250269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1251269Sericheng 1252269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1253269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1254269Sericheng dl_err = DL_OUTSTATE; 1255269Sericheng goto failed; 1256269Sericheng } 1257269Sericheng } 1258269Sericheng upgraded = B_TRUE; 1259269Sericheng 12601184Skrgopi switch (poll.dls_flags) { 1261269Sericheng default: 1262269Sericheng /*FALLTHRU*/ 1263269Sericheng case POLL_DISABLE: 1264269Sericheng proto_poll_disable(dsp); 1265269Sericheng break; 1266269Sericheng 1267269Sericheng case POLL_ENABLE: 1268269Sericheng ASSERT(!(dld_opt & DLD_OPT_NO_POLL)); 1269269Sericheng 1270269Sericheng /* 1271269Sericheng * Make sure polling is disabled. 1272269Sericheng */ 1273269Sericheng proto_poll_disable(dsp); 1274269Sericheng 1275269Sericheng /* 1276269Sericheng * Now attempt enable it. 1277269Sericheng */ 12781184Skrgopi if (check_ip_above(dsp->ds_rq) && 12791184Skrgopi proto_poll_enable(dsp, &poll)) { 12801184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 12811184Skrgopi poll.dls_flags = POLL_ENABLE; 12821184Skrgopi } 1283269Sericheng break; 1284269Sericheng } 1285269Sericheng 12861184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 12871184Skrgopi bcopy(&poll, pollp, sizeof (dl_capab_dls_t)); 12881184Skrgopi break; 12891184Skrgopi } 12901184Skrgopi case DL_CAPAB_SOFT_RING: { 12911184Skrgopi dl_capab_dls_t *soft_ringp; 12921184Skrgopi dl_capab_dls_t soft_ring; 12931184Skrgopi 12941184Skrgopi soft_ringp = (dl_capab_dls_t *)&sp[1]; 12951184Skrgopi /* 12961184Skrgopi * Copy for alignment. 12971184Skrgopi */ 12981184Skrgopi bcopy(soft_ringp, &soft_ring, 12991184Skrgopi sizeof (dl_capab_dls_t)); 13001184Skrgopi 13011184Skrgopi /* 13021184Skrgopi * We need to become writer before enabling and/or 13031184Skrgopi * disabling the soft_ring interface. If we couldn' 13041184Skrgopi * upgrade, check state again after re-acquiring the 13051184Skrgopi * lock to make sure we can proceed. 13061184Skrgopi */ 13071184Skrgopi if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 13081184Skrgopi rw_exit(&dsp->ds_lock); 13091184Skrgopi rw_enter(&dsp->ds_lock, RW_WRITER); 13101184Skrgopi 13111184Skrgopi if (dsp->ds_dlstate == DL_UNATTACHED || 13121184Skrgopi DL_ACK_PENDING(dsp->ds_dlstate)) { 13131184Skrgopi dl_err = DL_OUTSTATE; 13141184Skrgopi goto failed; 13151184Skrgopi } 13161184Skrgopi } 13171184Skrgopi upgraded = B_TRUE; 13181184Skrgopi 13191184Skrgopi switch (soft_ring.dls_flags) { 13201184Skrgopi default: 13211184Skrgopi /*FALLTHRU*/ 13221184Skrgopi case SOFT_RING_DISABLE: 13231184Skrgopi proto_soft_ring_disable(dsp); 13241184Skrgopi break; 13251184Skrgopi 13261184Skrgopi case SOFT_RING_ENABLE: 1327*4114Sja97890 ASSERT(!(dld_opt & DLD_OPT_NO_SOFTRING)); 13281184Skrgopi /* 13291184Skrgopi * Make sure soft_ring is disabled. 13301184Skrgopi */ 13311184Skrgopi proto_soft_ring_disable(dsp); 13321184Skrgopi 13331184Skrgopi /* 13341184Skrgopi * Now attempt enable it. 13351184Skrgopi */ 13361184Skrgopi if (check_ip_above(dsp->ds_rq) && 13371184Skrgopi proto_soft_ring_enable(dsp, &soft_ring)) { 13381184Skrgopi bzero(&soft_ring, 13391184Skrgopi sizeof (dl_capab_dls_t)); 13401184Skrgopi soft_ring.dls_flags = 13411184Skrgopi SOFT_RING_ENABLE; 13421184Skrgopi } else { 13431184Skrgopi bzero(&soft_ring, 13441184Skrgopi sizeof (dl_capab_dls_t)); 13451184Skrgopi soft_ring.dls_flags = 13461184Skrgopi SOFT_RING_DISABLE; 13471184Skrgopi } 13481184Skrgopi break; 13491184Skrgopi } 13501184Skrgopi 13511184Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 13521184Skrgopi bcopy(&soft_ring, soft_ringp, 13531184Skrgopi sizeof (dl_capab_dls_t)); 1354269Sericheng break; 1355269Sericheng } 1356269Sericheng default: 1357269Sericheng break; 1358269Sericheng } 1359269Sericheng 1360269Sericheng off += size; 1361269Sericheng } 1362269Sericheng rw_exit(&dsp->ds_lock); 1363269Sericheng qreply(q, mp); 1364269Sericheng return (B_TRUE); 1365269Sericheng failed: 1366269Sericheng rw_exit(&dsp->ds_lock); 1367269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 1368269Sericheng return (B_FALSE); 13690Sstevel@tonic-gate } 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate /* 1372269Sericheng * DL_NOTIFY_REQ 13730Sstevel@tonic-gate */ 1374269Sericheng static boolean_t 1375269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 13760Sstevel@tonic-gate { 1377269Sericheng dl_notify_req_t *dlp = (dl_notify_req_t *)udlp; 1378269Sericheng t_uscalar_t dl_err; 1379269Sericheng queue_t *q = dsp->ds_wq; 1380269Sericheng uint_t note = 1381269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1382269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1383269Sericheng DL_NOTE_PHYS_ADDR | 1384269Sericheng DL_NOTE_LINK_UP | 1385269Sericheng DL_NOTE_LINK_DOWN | 13862311Sseb DL_NOTE_CAPAB_RENEG | 13872311Sseb DL_NOTE_SPEED; 13880Sstevel@tonic-gate 13891521Syz147064 rw_enter(&dsp->ds_lock, RW_WRITER); 13901521Syz147064 1391269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1392269Sericheng dl_err = DL_BADPRIM; 1393269Sericheng goto failed; 1394269Sericheng } 13950Sstevel@tonic-gate 1396269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1397269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1398269Sericheng dl_err = DL_OUTSTATE; 1399269Sericheng goto failed; 14000Sstevel@tonic-gate } 14010Sstevel@tonic-gate 1402269Sericheng /* 1403269Sericheng * Cache the notifications that are being enabled. 1404269Sericheng */ 1405269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1406269Sericheng rw_exit(&dsp->ds_lock); 1407269Sericheng /* 1408269Sericheng * The ACK carries all notifications regardless of which set is 1409269Sericheng * being enabled. 1410269Sericheng */ 1411269Sericheng dlnotifyack(q, mp, note); 1412269Sericheng 1413269Sericheng /* 1414269Sericheng * Solicit DL_NOTIFY_IND messages for each enabled notification. 1415269Sericheng */ 1416269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1417269Sericheng if (dsp->ds_notifications != 0) { 1418269Sericheng rw_exit(&dsp->ds_lock); 1419269Sericheng dld_str_notify_ind(dsp); 1420269Sericheng } else { 1421269Sericheng rw_exit(&dsp->ds_lock); 1422269Sericheng } 1423269Sericheng return (B_TRUE); 1424269Sericheng failed: 1425269Sericheng rw_exit(&dsp->ds_lock); 1426269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 1427269Sericheng return (B_FALSE); 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate 14300Sstevel@tonic-gate /* 1431269Sericheng * DL_UINTDATA_REQ 14320Sstevel@tonic-gate */ 1433269Sericheng static boolean_t 1434269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 14350Sstevel@tonic-gate { 1436269Sericheng queue_t *q = dsp->ds_wq; 1437269Sericheng dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)udlp; 1438269Sericheng off_t off; 1439269Sericheng size_t len, size; 1440269Sericheng const uint8_t *addr; 1441269Sericheng uint16_t sap; 1442269Sericheng uint_t addr_length; 14432311Sseb mblk_t *bp, *payload; 1444269Sericheng uint32_t start, stuff, end, value, flags; 1445269Sericheng t_uscalar_t dl_err; 1446269Sericheng 1447269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1448269Sericheng 1449269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1450269Sericheng dl_err = DL_BADPRIM; 1451269Sericheng goto failed; 1452269Sericheng } 1453269Sericheng 1454269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 1455269Sericheng dl_err = DL_OUTSTATE; 1456269Sericheng goto failed; 1457269Sericheng } 1458269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1459269Sericheng 1460269Sericheng off = dlp->dl_dest_addr_offset; 1461269Sericheng len = dlp->dl_dest_addr_length; 1462269Sericheng 1463269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1464269Sericheng dl_err = DL_BADPRIM; 1465269Sericheng goto failed; 1466269Sericheng } 1467269Sericheng 1468269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1469269Sericheng dl_err = DL_BADADDR; 1470269Sericheng goto failed; 1471269Sericheng } 1472269Sericheng 1473269Sericheng addr = mp->b_rptr + off; 1474269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1475269Sericheng 1476269Sericheng /* 1477269Sericheng * Check the length of the packet and the block types. 1478269Sericheng */ 1479269Sericheng size = 0; 14802311Sseb payload = mp->b_cont; 14812311Sseb for (bp = payload; bp != NULL; bp = bp->b_cont) { 1482269Sericheng if (DB_TYPE(bp) != M_DATA) 1483269Sericheng goto baddata; 1484269Sericheng 1485269Sericheng size += MBLKL(bp); 1486269Sericheng } 1487269Sericheng 1488269Sericheng if (size > dsp->ds_mip->mi_sdu_max) 1489269Sericheng goto baddata; 1490269Sericheng 1491269Sericheng /* 1492269Sericheng * Build a packet header. 1493269Sericheng */ 14942760Sdg199075 if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max, 14952760Sdg199075 &payload)) == NULL) { 1496269Sericheng dl_err = DL_BADADDR; 1497269Sericheng goto failed; 1498269Sericheng } 1499269Sericheng 1500269Sericheng /* 1501269Sericheng * We no longer need the M_PROTO header, so free it. 1502269Sericheng */ 1503269Sericheng freeb(mp); 1504269Sericheng 1505269Sericheng /* 1506269Sericheng * Transfer the checksum offload information if it is present. 1507269Sericheng */ 15082311Sseb hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value, 1509269Sericheng &flags); 15102311Sseb (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0); 1511269Sericheng 1512269Sericheng /* 1513269Sericheng * Link the payload onto the new header. 1514269Sericheng */ 1515269Sericheng ASSERT(bp->b_cont == NULL); 15162311Sseb bp->b_cont = payload; 1517269Sericheng 15182760Sdg199075 dld_tx_single(dsp, bp); 1519269Sericheng rw_exit(&dsp->ds_lock); 1520269Sericheng return (B_TRUE); 1521269Sericheng failed: 1522269Sericheng rw_exit(&dsp->ds_lock); 1523269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1524269Sericheng return (B_FALSE); 1525269Sericheng 1526269Sericheng baddata: 1527269Sericheng rw_exit(&dsp->ds_lock); 1528269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1529269Sericheng return (B_FALSE); 1530269Sericheng } 1531269Sericheng 1532269Sericheng /* 1533269Sericheng * DL_PASSIVE_REQ 1534269Sericheng */ 1535269Sericheng /* ARGSUSED */ 1536269Sericheng static boolean_t 1537269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1538269Sericheng { 1539269Sericheng t_uscalar_t dl_err; 1540269Sericheng 1541269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1542269Sericheng /* 1543269Sericheng * If we've already become active by issuing an active primitive, 1544269Sericheng * then it's too late to try to become passive. 1545269Sericheng */ 1546269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1547269Sericheng dl_err = DL_OUTSTATE; 1548269Sericheng goto failed; 1549269Sericheng } 1550269Sericheng 1551269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1552269Sericheng dl_err = DL_BADPRIM; 1553269Sericheng goto failed; 1554269Sericheng } 1555269Sericheng 1556269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1557269Sericheng rw_exit(&dsp->ds_lock); 1558269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1559269Sericheng return (B_TRUE); 1560269Sericheng failed: 1561269Sericheng rw_exit(&dsp->ds_lock); 1562269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1563269Sericheng return (B_FALSE); 1564269Sericheng } 1565269Sericheng 1566269Sericheng 1567269Sericheng /* 1568269Sericheng * Catch-all handler. 1569269Sericheng */ 1570269Sericheng static boolean_t 1571269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp) 1572269Sericheng { 1573269Sericheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1574269Sericheng return (B_FALSE); 15750Sstevel@tonic-gate } 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate static void 15780Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp) 15790Sstevel@tonic-gate { 15800Sstevel@tonic-gate mac_handle_t mh; 15810Sstevel@tonic-gate 15821353Sericheng ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock)); 1583269Sericheng 15840Sstevel@tonic-gate if (!dsp->ds_polling) 15850Sstevel@tonic-gate return; 15860Sstevel@tonic-gate 15870Sstevel@tonic-gate /* 15880Sstevel@tonic-gate * It should be impossible to enable raw mode if polling is turned on. 15890Sstevel@tonic-gate */ 15900Sstevel@tonic-gate ASSERT(dsp->ds_mode != DLD_RAW); 15910Sstevel@tonic-gate 15920Sstevel@tonic-gate /* 15930Sstevel@tonic-gate * Reset the resource_add callback. 15940Sstevel@tonic-gate */ 15950Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 15960Sstevel@tonic-gate mac_resource_set(mh, NULL, NULL); 15971184Skrgopi mac_resources(mh); 15980Sstevel@tonic-gate 15990Sstevel@tonic-gate /* 16000Sstevel@tonic-gate * Set receive function back to default. 16010Sstevel@tonic-gate */ 16020Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ? 16030Sstevel@tonic-gate dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 16040Sstevel@tonic-gate 16050Sstevel@tonic-gate /* 16060Sstevel@tonic-gate * Note that polling is disabled. 16070Sstevel@tonic-gate */ 16080Sstevel@tonic-gate dsp->ds_polling = B_FALSE; 16090Sstevel@tonic-gate } 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate static boolean_t 16121184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp) 16130Sstevel@tonic-gate { 16140Sstevel@tonic-gate mac_handle_t mh; 16150Sstevel@tonic-gate 1616269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16170Sstevel@tonic-gate ASSERT(!dsp->ds_polling); 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate /* 16200Sstevel@tonic-gate * We cannot enable polling if raw mode 16210Sstevel@tonic-gate * has been enabled. 16220Sstevel@tonic-gate */ 16230Sstevel@tonic-gate if (dsp->ds_mode == DLD_RAW) 16240Sstevel@tonic-gate return (B_FALSE); 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 16270Sstevel@tonic-gate 16280Sstevel@tonic-gate /* 16290Sstevel@tonic-gate * Register resources. 16300Sstevel@tonic-gate */ 16311184Skrgopi mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add, 16321184Skrgopi (void *)pollp->dls_rx_handle); 16330Sstevel@tonic-gate mac_resources(mh); 16340Sstevel@tonic-gate 16350Sstevel@tonic-gate /* 16360Sstevel@tonic-gate * Set the receive function. 16370Sstevel@tonic-gate */ 16381184Skrgopi dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx, 16391184Skrgopi (void *)pollp->dls_rx_handle); 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate /* 16420Sstevel@tonic-gate * Note that polling is enabled. This prevents further DLIOCHDRINFO 16430Sstevel@tonic-gate * ioctls from overwriting the receive function pointer. 16440Sstevel@tonic-gate */ 16450Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 16460Sstevel@tonic-gate return (B_TRUE); 16470Sstevel@tonic-gate } 16480Sstevel@tonic-gate 16491184Skrgopi static void 16501184Skrgopi proto_soft_ring_disable(dld_str_t *dsp) 16511184Skrgopi { 16521184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16531184Skrgopi 16541184Skrgopi if (!dsp->ds_soft_ring) 16551184Skrgopi return; 16561184Skrgopi 16571184Skrgopi /* 16581184Skrgopi * It should be impossible to enable raw mode if soft_ring is turned on. 16591184Skrgopi */ 16601184Skrgopi ASSERT(dsp->ds_mode != DLD_RAW); 16611184Skrgopi proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE); 16621184Skrgopi /* 16631184Skrgopi * Note that fanout is disabled. 16641184Skrgopi */ 16651184Skrgopi dsp->ds_soft_ring = B_FALSE; 16661184Skrgopi } 16671184Skrgopi 16681184Skrgopi static boolean_t 16691184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp) 16701184Skrgopi { 16711184Skrgopi ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 16721184Skrgopi ASSERT(!dsp->ds_soft_ring); 16731184Skrgopi 16741184Skrgopi /* 16751184Skrgopi * We cannot enable soft_ring if raw mode 16761184Skrgopi * has been enabled. 16771184Skrgopi */ 16781184Skrgopi if (dsp->ds_mode == DLD_RAW) 16791184Skrgopi return (B_FALSE); 16801184Skrgopi 16811184Skrgopi if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE) 16821184Skrgopi return (B_FALSE); 16831184Skrgopi 16841184Skrgopi dsp->ds_soft_ring = B_TRUE; 16851184Skrgopi return (B_TRUE); 16861184Skrgopi } 16871184Skrgopi 16881184Skrgopi static void 16891184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type) 16901184Skrgopi { 16911184Skrgopi dls_rx_t rx; 16921184Skrgopi 16931184Skrgopi if (type == SOFT_RING_NONE) { 16941184Skrgopi rx = (dsp->ds_mode == DLD_FASTPATH) ? 16951184Skrgopi dld_str_rx_fastpath : dld_str_rx_unitdata; 16961184Skrgopi } else { 16973844Skrgopi rx = (dls_rx_t)dls_soft_ring_fanout; 16981184Skrgopi } 16991184Skrgopi dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type); 17001184Skrgopi } 17011184Skrgopi 17020Sstevel@tonic-gate /* 17030Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 17040Sstevel@tonic-gate */ 1705269Sericheng static boolean_t 1706269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 17070Sstevel@tonic-gate { 17080Sstevel@tonic-gate dl_capability_ack_t *dlap; 17090Sstevel@tonic-gate dl_capability_sub_t *dlsp; 17100Sstevel@tonic-gate size_t subsize; 17111184Skrgopi dl_capab_dls_t poll; 17123115Syl150051 dl_capab_dls_t soft_ring; 17130Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 17143115Syl150051 dl_capab_lso_t lso; 17150Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 17160Sstevel@tonic-gate uint8_t *ptr; 17172311Sseb boolean_t cksum_cap; 17180Sstevel@tonic-gate boolean_t poll_cap; 17193115Syl150051 boolean_t lso_cap; 17203115Syl150051 mac_capab_lso_t mac_lso; 1721269Sericheng queue_t *q = dsp->ds_wq; 1722269Sericheng mblk_t *mp1; 1723269Sericheng 1724269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 17250Sstevel@tonic-gate 17260Sstevel@tonic-gate /* 17270Sstevel@tonic-gate * Initially assume no capabilities. 17280Sstevel@tonic-gate */ 17290Sstevel@tonic-gate subsize = 0; 17300Sstevel@tonic-gate 17311555Skrgopi /* 1732*4114Sja97890 * Advertize soft ring capability unless it has been explicitly 1733*4114Sja97890 * disabled. 17341555Skrgopi */ 1735*4114Sja97890 if (!(dld_opt & DLD_OPT_NO_SOFTRING)) { 17361555Skrgopi subsize += sizeof (dl_capability_sub_t) + 17371555Skrgopi sizeof (dl_capab_dls_t); 1738*4114Sja97890 } 17391184Skrgopi 17400Sstevel@tonic-gate /* 17410Sstevel@tonic-gate * Check if polling can be enabled on this interface. 17420Sstevel@tonic-gate * If advertising DL_CAPAB_POLL has not been explicitly disabled 17430Sstevel@tonic-gate * then reserve space for that capability. 17440Sstevel@tonic-gate */ 17452311Sseb poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) && 17460Sstevel@tonic-gate !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE)); 17470Sstevel@tonic-gate if (poll_cap) { 17480Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17491184Skrgopi sizeof (dl_capab_dls_t); 17500Sstevel@tonic-gate } 17510Sstevel@tonic-gate 17520Sstevel@tonic-gate /* 17530Sstevel@tonic-gate * If the MAC interface supports checksum offload then reserve 17540Sstevel@tonic-gate * space for the DL_CAPAB_HCKSUM capability. 17550Sstevel@tonic-gate */ 17562311Sseb if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM, 17572311Sseb &hcksum.hcksum_txflags)) { 17580Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17590Sstevel@tonic-gate sizeof (dl_capab_hcksum_t); 17600Sstevel@tonic-gate } 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate /* 17633115Syl150051 * If LSO is usable for MAC, reserve space for the DL_CAPAB_LSO 17643115Syl150051 * capability. 17653115Syl150051 */ 17663115Syl150051 if (lso_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { 17673115Syl150051 subsize += sizeof (dl_capability_sub_t) + 17683115Syl150051 sizeof (dl_capab_lso_t); 17693115Syl150051 } 17703115Syl150051 17713115Syl150051 /* 17720Sstevel@tonic-gate * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then 17730Sstevel@tonic-gate * reserve space for it. 17740Sstevel@tonic-gate */ 17750Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 17760Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 17770Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 17780Sstevel@tonic-gate } 17790Sstevel@tonic-gate 17800Sstevel@tonic-gate /* 1781269Sericheng * If there are no capabilities to advertise or if we 1782269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 17830Sstevel@tonic-gate */ 17841184Skrgopi if ((mp1 = reallocb(mp, 1785269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1786269Sericheng rw_exit(&dsp->ds_lock); 1787269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1788269Sericheng return (B_FALSE); 17890Sstevel@tonic-gate } 17900Sstevel@tonic-gate 1791269Sericheng mp = mp1; 1792269Sericheng DB_TYPE(mp) = M_PROTO; 1793269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1794269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 17950Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 17960Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 17970Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 17980Sstevel@tonic-gate dlap->dl_sub_length = subsize; 17990Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 18000Sstevel@tonic-gate 18010Sstevel@tonic-gate /* 18020Sstevel@tonic-gate * IP polling interface. 18030Sstevel@tonic-gate */ 18040Sstevel@tonic-gate if (poll_cap) { 18050Sstevel@tonic-gate /* 1806269Sericheng * Attempt to disable just in case this is a re-negotiation; 1807269Sericheng * we need to become writer before doing so. 18080Sstevel@tonic-gate */ 1809269Sericheng if (!rw_tryupgrade(&dsp->ds_lock)) { 1810269Sericheng rw_exit(&dsp->ds_lock); 1811269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1812269Sericheng } 18130Sstevel@tonic-gate 1814269Sericheng /* 1815269Sericheng * Check if polling state has changed after we re-acquired 1816269Sericheng * the lock above, so that we don't mis-advertise it. 1817269Sericheng */ 18182311Sseb poll_cap = !(dld_opt & DLD_OPT_NO_POLL) && 18192311Sseb (dsp->ds_vid == VLAN_ID_NONE); 18200Sstevel@tonic-gate 1821269Sericheng if (!poll_cap) { 1822269Sericheng int poll_capab_size; 1823269Sericheng 1824269Sericheng rw_downgrade(&dsp->ds_lock); 1825269Sericheng 1826269Sericheng poll_capab_size = sizeof (dl_capability_sub_t) + 18271184Skrgopi sizeof (dl_capab_dls_t); 18280Sstevel@tonic-gate 1829269Sericheng mp->b_wptr -= poll_capab_size; 1830269Sericheng subsize -= poll_capab_size; 1831269Sericheng dlap->dl_sub_length = subsize; 1832269Sericheng } else { 1833269Sericheng proto_poll_disable(dsp); 1834269Sericheng 1835269Sericheng rw_downgrade(&dsp->ds_lock); 1836269Sericheng 1837269Sericheng dlsp = (dl_capability_sub_t *)ptr; 1838269Sericheng 1839269Sericheng dlsp->dl_cap = DL_CAPAB_POLL; 18401184Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 1841269Sericheng ptr += sizeof (dl_capability_sub_t); 18420Sstevel@tonic-gate 18431184Skrgopi bzero(&poll, sizeof (dl_capab_dls_t)); 18441184Skrgopi poll.dls_version = POLL_VERSION_1; 18451184Skrgopi poll.dls_flags = POLL_CAPABLE; 18461184Skrgopi poll.dls_tx_handle = (uintptr_t)dsp; 18471184Skrgopi poll.dls_tx = (uintptr_t)str_mdata_fastpath_put; 1848269Sericheng 18491184Skrgopi dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq); 18501184Skrgopi bcopy(&poll, ptr, sizeof (dl_capab_dls_t)); 18511184Skrgopi ptr += sizeof (dl_capab_dls_t); 1852269Sericheng } 18530Sstevel@tonic-gate } 18540Sstevel@tonic-gate 1855269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 1856269Sericheng 1857*4114Sja97890 if (!(dld_opt & DLD_OPT_NO_SOFTRING)) { 18581555Skrgopi dlsp = (dl_capability_sub_t *)ptr; 18591184Skrgopi 18601555Skrgopi dlsp->dl_cap = DL_CAPAB_SOFT_RING; 18611555Skrgopi dlsp->dl_length = sizeof (dl_capab_dls_t); 18621555Skrgopi ptr += sizeof (dl_capability_sub_t); 18631184Skrgopi 18641555Skrgopi bzero(&soft_ring, sizeof (dl_capab_dls_t)); 18651555Skrgopi soft_ring.dls_version = SOFT_RING_VERSION_1; 18661555Skrgopi soft_ring.dls_flags = SOFT_RING_CAPABLE; 18671555Skrgopi soft_ring.dls_tx_handle = (uintptr_t)dsp; 18681555Skrgopi soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put; 18691555Skrgopi soft_ring.dls_ring_change_status = 18701555Skrgopi (uintptr_t)proto_change_soft_ring_fanout; 18711555Skrgopi soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind; 18721555Skrgopi soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind; 18731184Skrgopi 18741555Skrgopi dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq); 18751555Skrgopi bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t)); 18761555Skrgopi ptr += sizeof (dl_capab_dls_t); 18771555Skrgopi } 18781184Skrgopi 18790Sstevel@tonic-gate /* 18800Sstevel@tonic-gate * TCP/IP checksum offload. 18810Sstevel@tonic-gate */ 18822311Sseb if (cksum_cap) { 18830Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 18840Sstevel@tonic-gate 18850Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 18860Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 18870Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 18880Sstevel@tonic-gate 18890Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 18900Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 18910Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 18920Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 18930Sstevel@tonic-gate } 18940Sstevel@tonic-gate 18950Sstevel@tonic-gate /* 18963115Syl150051 * Large segment offload. (LSO) 18973115Syl150051 */ 18983115Syl150051 if (lso_cap) { 18993115Syl150051 dlsp = (dl_capability_sub_t *)ptr; 19003115Syl150051 19013115Syl150051 dlsp->dl_cap = DL_CAPAB_LSO; 19023115Syl150051 dlsp->dl_length = sizeof (dl_capab_lso_t); 19033115Syl150051 ptr += sizeof (dl_capability_sub_t); 19043115Syl150051 19053115Syl150051 lso.lso_version = LSO_VERSION_1; 19063115Syl150051 lso.lso_flags = mac_lso.lso_flags; 19073115Syl150051 lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; 19083115Syl150051 19093115Syl150051 /* Simply enable LSO with DLD */ 19103115Syl150051 dsp->ds_lso = B_TRUE; 19113115Syl150051 dsp->ds_lso_max = lso.lso_max; 19123115Syl150051 19133115Syl150051 dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq); 19143115Syl150051 bcopy(&lso, ptr, sizeof (dl_capab_lso_t)); 19153115Syl150051 ptr += sizeof (dl_capab_lso_t); 19163115Syl150051 } else { 19173115Syl150051 dsp->ds_lso = B_FALSE; 19183115Syl150051 dsp->ds_lso_max = 0; 19193115Syl150051 } 19203115Syl150051 19213115Syl150051 /* 19220Sstevel@tonic-gate * Zero copy 19230Sstevel@tonic-gate */ 19240Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 19250Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 19280Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 19290Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 19300Sstevel@tonic-gate 19310Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 19320Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 19330Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 19340Sstevel@tonic-gate 19350Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 19360Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 19370Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 19380Sstevel@tonic-gate } 19390Sstevel@tonic-gate 19400Sstevel@tonic-gate ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1941269Sericheng 1942269Sericheng rw_exit(&dsp->ds_lock); 1943269Sericheng qreply(q, mp); 1944269Sericheng return (B_TRUE); 19450Sstevel@tonic-gate } 1946