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 52311Sseb * Common Development and Distribution License (the "License"). 62311Sseb * 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 /* 223448Sdh155122 * 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/conf.h> 33269Sericheng #include <sys/mkdev.h> 34269Sericheng #include <sys/modctl.h> 350Sstevel@tonic-gate #include <sys/stat.h> 36269Sericheng #include <sys/strsun.h> 37269Sericheng #include <sys/dld.h> 38269Sericheng #include <sys/dld_impl.h> 39269Sericheng #include <sys/dls_impl.h> 403448Sdh155122 #include <sys/vlan.h> 410Sstevel@tonic-gate #include <inet/common.h> 420Sstevel@tonic-gate 43269Sericheng /* 44269Sericheng * dld control node state, one per open control node session. 45269Sericheng */ 46269Sericheng typedef struct dld_ctl_str_s { 47269Sericheng minor_t cs_minor; 48269Sericheng queue_t *cs_wq; 49269Sericheng } dld_ctl_str_t; 500Sstevel@tonic-gate 510Sstevel@tonic-gate static void drv_init(void); 520Sstevel@tonic-gate static int drv_fini(void); 530Sstevel@tonic-gate 540Sstevel@tonic-gate static int drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 550Sstevel@tonic-gate static int drv_attach(dev_info_t *, ddi_attach_cmd_t); 560Sstevel@tonic-gate static int drv_detach(dev_info_t *, ddi_detach_cmd_t); 570Sstevel@tonic-gate 58269Sericheng /* 593147Sxc151355 * Secure objects declarations 603147Sxc151355 */ 613147Sxc151355 #define SECOBJ_WEP_HASHSZ 67 623147Sxc151355 static krwlock_t drv_secobj_lock; 633147Sxc151355 static kmem_cache_t *drv_secobj_cachep; 643147Sxc151355 static mod_hash_t *drv_secobj_hash; 653147Sxc151355 static void drv_secobj_init(void); 663147Sxc151355 static void drv_secobj_fini(void); 673147Sxc151355 static void drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *); 683147Sxc151355 static void drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *); 693147Sxc151355 static void drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *); 703147Sxc151355 713147Sxc151355 /* 72269Sericheng * The following entry points are private to dld and are used for control 73269Sericheng * operations only. The entry points exported to mac drivers are defined 74269Sericheng * in dld_str.c. Refer to the comment on top of dld_str.c for details. 75269Sericheng */ 760Sstevel@tonic-gate static int drv_open(queue_t *, dev_t *, int, int, cred_t *); 770Sstevel@tonic-gate static int drv_close(queue_t *); 780Sstevel@tonic-gate 790Sstevel@tonic-gate static void drv_uw_put(queue_t *, mblk_t *); 800Sstevel@tonic-gate static void drv_uw_srv(queue_t *); 810Sstevel@tonic-gate 820Sstevel@tonic-gate dev_info_t *dld_dip; /* dev_info_t for the driver */ 83269Sericheng uint32_t dld_opt = 0; /* Global options */ 84269Sericheng static vmem_t *dld_ctl_vmem; /* for control minor numbers */ 850Sstevel@tonic-gate 860Sstevel@tonic-gate static struct module_info drv_info = { 870Sstevel@tonic-gate 0, /* mi_idnum */ 880Sstevel@tonic-gate DLD_DRIVER_NAME, /* mi_idname */ 890Sstevel@tonic-gate 0, /* mi_minpsz */ 900Sstevel@tonic-gate (64 * 1024), /* mi_maxpsz */ 910Sstevel@tonic-gate 1, /* mi_hiwat */ 920Sstevel@tonic-gate 0 /* mi_lowat */ 930Sstevel@tonic-gate }; 940Sstevel@tonic-gate 950Sstevel@tonic-gate static struct qinit drv_ur_init = { 960Sstevel@tonic-gate NULL, /* qi_putp */ 970Sstevel@tonic-gate NULL, /* qi_srvp */ 980Sstevel@tonic-gate drv_open, /* qi_qopen */ 990Sstevel@tonic-gate drv_close, /* qi_qclose */ 1000Sstevel@tonic-gate NULL, /* qi_qadmin */ 1010Sstevel@tonic-gate &drv_info, /* qi_minfo */ 1020Sstevel@tonic-gate NULL /* qi_mstat */ 1030Sstevel@tonic-gate }; 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate static struct qinit drv_uw_init = { 1060Sstevel@tonic-gate (pfi_t)drv_uw_put, /* qi_putp */ 1070Sstevel@tonic-gate (pfi_t)drv_uw_srv, /* qi_srvp */ 1080Sstevel@tonic-gate NULL, /* qi_qopen */ 1090Sstevel@tonic-gate NULL, /* qi_qclose */ 1100Sstevel@tonic-gate NULL, /* qi_qadmin */ 1110Sstevel@tonic-gate &drv_info, /* qi_minfo */ 1120Sstevel@tonic-gate NULL /* qi_mstat */ 1130Sstevel@tonic-gate }; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate static struct streamtab drv_stream = { 1160Sstevel@tonic-gate &drv_ur_init, /* st_rdinit */ 1170Sstevel@tonic-gate &drv_uw_init, /* st_wrinit */ 1180Sstevel@tonic-gate NULL, /* st_muxrinit */ 1190Sstevel@tonic-gate NULL /* st_muxwinit */ 1200Sstevel@tonic-gate }; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach, 123269Sericheng nodev, drv_getinfo, D_MP, &drv_stream); 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate /* 1260Sstevel@tonic-gate * Module linkage information for the kernel. 1270Sstevel@tonic-gate */ 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate extern struct mod_ops mod_driverops; 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate static struct modldrv drv_modldrv = { 1320Sstevel@tonic-gate &mod_driverops, 1330Sstevel@tonic-gate DLD_INFO, 1340Sstevel@tonic-gate &drv_ops 1350Sstevel@tonic-gate }; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate static struct modlinkage drv_modlinkage = { 1380Sstevel@tonic-gate MODREV_1, 1390Sstevel@tonic-gate &drv_modldrv, 1400Sstevel@tonic-gate NULL 1410Sstevel@tonic-gate }; 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate int 1440Sstevel@tonic-gate _init(void) 1450Sstevel@tonic-gate { 1460Sstevel@tonic-gate int err; 1470Sstevel@tonic-gate 148269Sericheng drv_init(); 149269Sericheng 1500Sstevel@tonic-gate if ((err = mod_install(&drv_modlinkage)) != 0) 1510Sstevel@tonic-gate return (err); 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate return (0); 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate int 1570Sstevel@tonic-gate _fini(void) 1580Sstevel@tonic-gate { 1590Sstevel@tonic-gate int err; 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate if ((err = mod_remove(&drv_modlinkage)) != 0) 1620Sstevel@tonic-gate return (err); 1630Sstevel@tonic-gate 164269Sericheng if (drv_fini() != 0) { 165269Sericheng (void) mod_install(&drv_modlinkage); 166269Sericheng return (DDI_FAILURE); 167269Sericheng } 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate return (err); 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate int 1730Sstevel@tonic-gate _info(struct modinfo *modinfop) 1740Sstevel@tonic-gate { 1750Sstevel@tonic-gate return (mod_info(&drv_modlinkage, modinfop)); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 179269Sericheng * Initialize component modules. 1800Sstevel@tonic-gate */ 1810Sstevel@tonic-gate static void 1820Sstevel@tonic-gate drv_init(void) 1830Sstevel@tonic-gate { 184269Sericheng dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1, 185269Sericheng NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER); 1863147Sxc151355 drv_secobj_init(); 1870Sstevel@tonic-gate dld_str_init(); 1880Sstevel@tonic-gate } 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate static int 1910Sstevel@tonic-gate drv_fini(void) 1920Sstevel@tonic-gate { 1930Sstevel@tonic-gate int err; 1940Sstevel@tonic-gate 195269Sericheng if ((err = dld_str_fini()) != 0) 1960Sstevel@tonic-gate return (err); 1970Sstevel@tonic-gate 1983147Sxc151355 drv_secobj_fini(); 199269Sericheng vmem_destroy(dld_ctl_vmem); 2000Sstevel@tonic-gate return (0); 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate /* 2040Sstevel@tonic-gate * devo_getinfo: getinfo(9e) 2050Sstevel@tonic-gate */ 2060Sstevel@tonic-gate /*ARGSUSED*/ 2070Sstevel@tonic-gate static int 2080Sstevel@tonic-gate drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp) 2090Sstevel@tonic-gate { 2100Sstevel@tonic-gate if (dld_dip == NULL) 2110Sstevel@tonic-gate return (DDI_FAILURE); 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate switch (cmd) { 2140Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 2150Sstevel@tonic-gate *resp = (void *)0; 2160Sstevel@tonic-gate break; 2170Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 2180Sstevel@tonic-gate *resp = (void *)dld_dip; 2190Sstevel@tonic-gate break; 2200Sstevel@tonic-gate default: 2210Sstevel@tonic-gate return (DDI_FAILURE); 2220Sstevel@tonic-gate } 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate return (DDI_SUCCESS); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate /* 2280Sstevel@tonic-gate * Check properties to set options. (See dld.h for property definitions). 2290Sstevel@tonic-gate */ 2300Sstevel@tonic-gate static void 2310Sstevel@tonic-gate drv_set_opt(dev_info_t *dip) 2320Sstevel@tonic-gate { 2330Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2340Sstevel@tonic-gate DLD_PROP_NO_FASTPATH, 0) != 0) { 2350Sstevel@tonic-gate dld_opt |= DLD_OPT_NO_FASTPATH; 2360Sstevel@tonic-gate } 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2390Sstevel@tonic-gate DLD_PROP_NO_POLL, 0) != 0) { 2400Sstevel@tonic-gate dld_opt |= DLD_OPT_NO_POLL; 2410Sstevel@tonic-gate } 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2440Sstevel@tonic-gate DLD_PROP_NO_ZEROCOPY, 0) != 0) { 2450Sstevel@tonic-gate dld_opt |= DLD_OPT_NO_ZEROCOPY; 2460Sstevel@tonic-gate } 2474114Sja97890 2484114Sja97890 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2494114Sja97890 DLD_PROP_NO_SOFTRING, 0) != 0) { 2504114Sja97890 dld_opt |= DLD_OPT_NO_SOFTRING; 2514114Sja97890 } 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /* 2550Sstevel@tonic-gate * devo_attach: attach(9e) 2560Sstevel@tonic-gate */ 2570Sstevel@tonic-gate static int 2580Sstevel@tonic-gate drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2590Sstevel@tonic-gate { 2600Sstevel@tonic-gate if (cmd != DDI_ATTACH) 2610Sstevel@tonic-gate return (DDI_FAILURE); 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate ASSERT(ddi_get_instance(dip) == 0); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate drv_set_opt(dip); 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate /* 2680Sstevel@tonic-gate * Create control node. DLPI provider nodes will be created on demand. 2690Sstevel@tonic-gate */ 2700Sstevel@tonic-gate if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR, 2710Sstevel@tonic-gate DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) 2720Sstevel@tonic-gate return (DDI_FAILURE); 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate dld_dip = dip; 2750Sstevel@tonic-gate 2760Sstevel@tonic-gate /* 2770Sstevel@tonic-gate * Log the fact that the driver is now attached. 2780Sstevel@tonic-gate */ 2790Sstevel@tonic-gate ddi_report_dev(dip); 2800Sstevel@tonic-gate return (DDI_SUCCESS); 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate /* 2840Sstevel@tonic-gate * devo_detach: detach(9e) 2850Sstevel@tonic-gate */ 2860Sstevel@tonic-gate static int 2870Sstevel@tonic-gate drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2880Sstevel@tonic-gate { 2890Sstevel@tonic-gate if (cmd != DDI_DETACH) 2900Sstevel@tonic-gate return (DDI_FAILURE); 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate ASSERT(dld_dip == dip); 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate /* 2950Sstevel@tonic-gate * Remove the control node. 2960Sstevel@tonic-gate */ 2970Sstevel@tonic-gate ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME); 2980Sstevel@tonic-gate dld_dip = NULL; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate return (DDI_SUCCESS); 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate /* 304269Sericheng * dld control node open procedure. 3050Sstevel@tonic-gate */ 3060Sstevel@tonic-gate /*ARGSUSED*/ 3070Sstevel@tonic-gate static int 3080Sstevel@tonic-gate drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 3090Sstevel@tonic-gate { 310269Sericheng dld_ctl_str_t *ctls; 3110Sstevel@tonic-gate minor_t minor; 312269Sericheng queue_t *oq = OTHERQ(rq); 3130Sstevel@tonic-gate 314269Sericheng if (sflag == MODOPEN) 315269Sericheng return (ENOTSUP); 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * This is a cloning driver and therefore each queue should only 3190Sstevel@tonic-gate * ever get opened once. 3200Sstevel@tonic-gate */ 3210Sstevel@tonic-gate if (rq->q_ptr != NULL) 3220Sstevel@tonic-gate return (EBUSY); 3230Sstevel@tonic-gate 324269Sericheng minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP); 325269Sericheng if (minor == 0) 326269Sericheng return (ENOMEM); 3270Sstevel@tonic-gate 328269Sericheng ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP); 329269Sericheng if (ctls == NULL) { 330269Sericheng vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1); 331269Sericheng return (ENOMEM); 332269Sericheng } 3330Sstevel@tonic-gate 334269Sericheng ctls->cs_minor = minor; 335269Sericheng ctls->cs_wq = WR(rq); 3360Sstevel@tonic-gate 337269Sericheng rq->q_ptr = ctls; 338269Sericheng oq->q_ptr = ctls; 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate /* 3410Sstevel@tonic-gate * Enable the queue srv(9e) routine. 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate qprocson(rq); 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate /* 3460Sstevel@tonic-gate * Construct a cloned dev_t to hand back. 3470Sstevel@tonic-gate */ 348269Sericheng *devp = makedevice(getmajor(*devp), ctls->cs_minor); 3490Sstevel@tonic-gate return (0); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate /* 353269Sericheng * dld control node close procedure. 3540Sstevel@tonic-gate */ 3550Sstevel@tonic-gate static int 3560Sstevel@tonic-gate drv_close(queue_t *rq) 3570Sstevel@tonic-gate { 358269Sericheng dld_ctl_str_t *ctls; 3590Sstevel@tonic-gate 360269Sericheng ctls = rq->q_ptr; 361269Sericheng ASSERT(ctls != NULL); 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* 3640Sstevel@tonic-gate * Disable the queue srv(9e) routine. 3650Sstevel@tonic-gate */ 3660Sstevel@tonic-gate qprocsoff(rq); 3670Sstevel@tonic-gate 368269Sericheng vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1); 3690Sstevel@tonic-gate 370269Sericheng kmem_free(ctls, sizeof (dld_ctl_str_t)); 371269Sericheng 3720Sstevel@tonic-gate return (0); 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate /* 376269Sericheng * DLDIOCATTR 3770Sstevel@tonic-gate */ 3780Sstevel@tonic-gate static void 379269Sericheng drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp) 3800Sstevel@tonic-gate { 3812311Sseb dld_ioc_attr_t *diap; 382269Sericheng dls_vlan_t *dvp = NULL; 383269Sericheng dls_link_t *dlp = NULL; 384269Sericheng int err; 385269Sericheng queue_t *q = ctls->cs_wq; 386269Sericheng 387269Sericheng if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0) 388269Sericheng goto failed; 389269Sericheng 390269Sericheng diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr; 391269Sericheng diap->dia_name[IFNAMSIZ - 1] = '\0'; 392269Sericheng 393269Sericheng if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) { 394269Sericheng err = ENOENT; 395269Sericheng goto failed; 396269Sericheng } 397269Sericheng 398269Sericheng dlp = dvp->dv_dlp; 3992311Sseb (void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev)); 400269Sericheng diap->dia_vid = dvp->dv_id; 401269Sericheng diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max; 402269Sericheng 403269Sericheng dls_vlan_rele(dvp); 404269Sericheng miocack(q, mp, sizeof (dld_ioc_attr_t), 0); 405269Sericheng return; 406269Sericheng 407269Sericheng failed: 408269Sericheng ASSERT(err != 0); 409269Sericheng if (err == ENOENT) { 410269Sericheng char devname[MAXNAMELEN]; 411269Sericheng uint_t instance; 412269Sericheng major_t major; 413269Sericheng 414269Sericheng /* 415269Sericheng * Try to detect if the specified device is gldv3 416269Sericheng * and return ENODEV if it is not. 417269Sericheng */ 418269Sericheng if (ddi_parse(diap->dia_name, devname, &instance) == 0 && 419269Sericheng (major = ddi_name_to_major(devname)) != (major_t)-1 && 420269Sericheng !GLDV3_DRV(major)) 421269Sericheng err = ENODEV; 422269Sericheng } 423269Sericheng miocnak(q, mp, 0, err); 424269Sericheng } 425269Sericheng 426269Sericheng 427269Sericheng /* 428269Sericheng * DLDIOCVLAN 429269Sericheng */ 430269Sericheng typedef struct dld_ioc_vlan_state { 431269Sericheng uint_t bytes_left; 432733Skrgopi dld_ioc_vlan_t *divp; 433269Sericheng dld_vlan_info_t *vlanp; 434269Sericheng } dld_ioc_vlan_state_t; 435269Sericheng 436269Sericheng static int 437269Sericheng drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg) 438269Sericheng { 439269Sericheng dld_ioc_vlan_state_t *statep = arg; 4400Sstevel@tonic-gate 441733Skrgopi /* 442733Skrgopi * passed buffer space is limited to 65536 bytes. So 443733Skrgopi * copy only the vlans associated with the passed link. 444733Skrgopi */ 4452311Sseb if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 && 446733Skrgopi dvp->dv_id != 0) { 447733Skrgopi if (statep->bytes_left < sizeof (dld_vlan_info_t)) 448733Skrgopi return (ENOSPC); 449269Sericheng 450733Skrgopi (void) strlcpy(statep->vlanp->dvi_name, 451733Skrgopi dvp->dv_name, IFNAMSIZ); 452733Skrgopi statep->divp->div_count++; 453733Skrgopi statep->bytes_left -= sizeof (dld_vlan_info_t); 454733Skrgopi statep->vlanp += 1; 455733Skrgopi } 456269Sericheng return (0); 457269Sericheng } 458269Sericheng 459269Sericheng static void 460269Sericheng drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 461269Sericheng { 462269Sericheng dld_ioc_vlan_t *divp; 463269Sericheng dld_ioc_vlan_state_t state; 464269Sericheng int err = EINVAL; 465269Sericheng queue_t *q = ctls->cs_wq; 466733Skrgopi mblk_t *bp; 467269Sericheng 468269Sericheng if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0) 469269Sericheng goto failed; 470269Sericheng 471733Skrgopi if ((bp = msgpullup(mp->b_cont, -1)) == NULL) 472733Skrgopi goto failed; 473733Skrgopi 474733Skrgopi freemsg(mp->b_cont); 475733Skrgopi mp->b_cont = bp; 476733Skrgopi divp = (dld_ioc_vlan_t *)bp->b_rptr; 477733Skrgopi divp->div_count = 0; 478733Skrgopi state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t); 479733Skrgopi state.divp = divp; 480269Sericheng state.vlanp = (dld_vlan_info_t *)(divp + 1); 481269Sericheng 482269Sericheng err = dls_vlan_walk(drv_ioc_vlan_info, &state); 483269Sericheng if (err != 0) 484269Sericheng goto failed; 4850Sstevel@tonic-gate 486269Sericheng miocack(q, mp, sizeof (dld_ioc_vlan_t) + 487733Skrgopi state.divp->div_count * sizeof (dld_vlan_info_t), 0); 488269Sericheng return; 489269Sericheng 490269Sericheng failed: 491269Sericheng ASSERT(err != 0); 492269Sericheng miocnak(q, mp, 0, err); 493269Sericheng } 494269Sericheng 4953448Sdh155122 /* 4963448Sdh155122 * DLDIOCHOLDVLAN 4973448Sdh155122 */ 4983448Sdh155122 static void 4993448Sdh155122 drv_hold_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 5003448Sdh155122 { 5013448Sdh155122 queue_t *q = ctls->cs_wq; 5023448Sdh155122 dld_hold_vlan_t *dhv; 5033448Sdh155122 mblk_t *nmp; 5044049Sdh155122 int err = EINVAL; 5053448Sdh155122 dls_vlan_t *dvp; 5064049Sdh155122 char mac[MAXNAMELEN]; 5074049Sdh155122 dev_info_t *dip = NULL; 5084049Sdh155122 major_t major; 5094049Sdh155122 uint_t index; 5103448Sdh155122 5113448Sdh155122 nmp = mp->b_cont; 5124049Sdh155122 if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) 5134049Sdh155122 goto failed; 5144049Sdh155122 5153448Sdh155122 dhv = (dld_hold_vlan_t *)nmp->b_rptr; 5163448Sdh155122 5174049Sdh155122 /* 5184049Sdh155122 * When a device instance without opens is detached, its 5194049Sdh155122 * dls_vlan_t will be destroyed. A subsequent DLDIOCHOLDVLAN 5204049Sdh155122 * invoked on this device instance will fail because 5214049Sdh155122 * dls_vlan_hold() does not create non-tagged vlans on demand. 5224049Sdh155122 * To handle this problem, we must force the creation of the 5234049Sdh155122 * dls_vlan_t (if it doesn't already exist) by calling 5244049Sdh155122 * ddi_hold_devi_by_instance() before calling dls_vlan_hold(). 5254049Sdh155122 */ 5264049Sdh155122 if (ddi_parse(dhv->dhv_name, mac, &index) != DDI_SUCCESS) 5274049Sdh155122 goto failed; 5284049Sdh155122 5294049Sdh155122 if (DLS_PPA2VID(index) == VLAN_ID_NONE && strcmp(mac, "aggr") != 0) { 5304049Sdh155122 if ((major = ddi_name_to_major(mac)) == (major_t)-1 || 5314049Sdh155122 (dip = ddi_hold_devi_by_instance(major, 5324049Sdh155122 DLS_PPA2INST(index), 0)) == NULL) 5334049Sdh155122 goto failed; 5343448Sdh155122 } 5353448Sdh155122 5364049Sdh155122 err = dls_vlan_hold(dhv->dhv_name, &dvp, B_TRUE); 5374049Sdh155122 if (dip != NULL) 5384049Sdh155122 ddi_release_devi(dip); 5394049Sdh155122 5404049Sdh155122 if (err != 0) 5414049Sdh155122 goto failed; 5424049Sdh155122 5433448Sdh155122 if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid, 5443871Syz147064 dhv->dhv_docheck)) != 0) { 5453871Syz147064 dls_vlan_rele(dvp); 5464049Sdh155122 goto failed; 5473871Syz147064 } else { 5483448Sdh155122 miocack(q, mp, 0, 0); 5494049Sdh155122 return; 5503871Syz147064 } 5514049Sdh155122 failed: 5524049Sdh155122 miocnak(q, mp, 0, err); 5533448Sdh155122 } 5543448Sdh155122 5553448Sdh155122 /* 5563448Sdh155122 * DLDIOCRELEVLAN 5573448Sdh155122 */ 5583448Sdh155122 static void 5593448Sdh155122 drv_rele_vlan(dld_ctl_str_t *ctls, mblk_t *mp) 5603448Sdh155122 { 5613448Sdh155122 queue_t *q = ctls->cs_wq; 5623448Sdh155122 dld_hold_vlan_t *dhv; 5633448Sdh155122 mblk_t *nmp; 5643448Sdh155122 int err; 5653448Sdh155122 5663448Sdh155122 nmp = mp->b_cont; 5673448Sdh155122 if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) { 5683448Sdh155122 err = EINVAL; 5693448Sdh155122 miocnak(q, mp, 0, err); 5703448Sdh155122 return; 5713448Sdh155122 } 5723448Sdh155122 dhv = (dld_hold_vlan_t *)nmp->b_rptr; 5733448Sdh155122 5743448Sdh155122 if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid, 5753448Sdh155122 dhv->dhv_docheck)) != 0) { 5763448Sdh155122 miocnak(q, mp, 0, err); 5773448Sdh155122 return; 5783448Sdh155122 } 5793448Sdh155122 5803448Sdh155122 if ((err = dls_vlan_rele_by_name(dhv->dhv_name)) != 0) { 5813448Sdh155122 miocnak(q, mp, 0, err); 5823448Sdh155122 return; 5833448Sdh155122 } 5843448Sdh155122 5853448Sdh155122 miocack(q, mp, 0, 0); 5863448Sdh155122 } 5873448Sdh155122 5883448Sdh155122 /* 5893448Sdh155122 * DLDIOCZIDGET 5903448Sdh155122 */ 5913448Sdh155122 static void 5923448Sdh155122 drv_ioc_zid_get(dld_ctl_str_t *ctls, mblk_t *mp) 5933448Sdh155122 { 5943448Sdh155122 queue_t *q = ctls->cs_wq; 5953448Sdh155122 dld_hold_vlan_t *dhv; 5963448Sdh155122 mblk_t *nmp; 5973448Sdh155122 int err; 5983448Sdh155122 5993448Sdh155122 nmp = mp->b_cont; 6003448Sdh155122 if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) { 6013448Sdh155122 err = EINVAL; 6023448Sdh155122 miocnak(q, mp, 0, err); 6033448Sdh155122 return; 6043448Sdh155122 } 6053448Sdh155122 dhv = (dld_hold_vlan_t *)nmp->b_rptr; 6063448Sdh155122 6073448Sdh155122 if ((err = dls_vlan_getzoneid(dhv->dhv_name, &dhv->dhv_zid)) != 0) 6083448Sdh155122 miocnak(q, mp, 0, err); 6093448Sdh155122 else 6103448Sdh155122 miocack(q, mp, sizeof (dld_hold_vlan_t), 0); 6113448Sdh155122 } 612269Sericheng 613269Sericheng /* 614269Sericheng * Process an IOCTL message received by the control node. 615269Sericheng */ 616269Sericheng static void 617269Sericheng drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp) 618269Sericheng { 619269Sericheng uint_t cmd; 620269Sericheng 621269Sericheng cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd; 622269Sericheng switch (cmd) { 623269Sericheng case DLDIOCATTR: 624269Sericheng drv_ioc_attr(ctls, mp); 625269Sericheng return; 626269Sericheng case DLDIOCVLAN: 627269Sericheng drv_ioc_vlan(ctls, mp); 628269Sericheng return; 6293147Sxc151355 case DLDIOCSECOBJSET: 6303147Sxc151355 drv_ioc_secobj_set(ctls, mp); 6313147Sxc151355 return; 6323147Sxc151355 case DLDIOCSECOBJGET: 6333147Sxc151355 drv_ioc_secobj_get(ctls, mp); 6343147Sxc151355 return; 6353147Sxc151355 case DLDIOCSECOBJUNSET: 6363147Sxc151355 drv_ioc_secobj_unset(ctls, mp); 6373147Sxc151355 return; 6383448Sdh155122 case DLDIOCHOLDVLAN: 6393448Sdh155122 drv_hold_vlan(ctls, mp); 6403448Sdh155122 return; 6413448Sdh155122 case DLDIOCRELEVLAN: 6423448Sdh155122 drv_rele_vlan(ctls, mp); 6433448Sdh155122 return; 6443448Sdh155122 case DLDIOCZIDGET: 6453448Sdh155122 drv_ioc_zid_get(ctls, mp); 6463448Sdh155122 return; 647269Sericheng default: 648269Sericheng miocnak(ctls->cs_wq, mp, 0, ENOTSUP); 649269Sericheng return; 650269Sericheng } 6510Sstevel@tonic-gate } 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate /* 654269Sericheng * Write side put routine of the dld control node. 6550Sstevel@tonic-gate */ 6560Sstevel@tonic-gate static void 657269Sericheng drv_uw_put(queue_t *q, mblk_t *mp) 6580Sstevel@tonic-gate { 659269Sericheng dld_ctl_str_t *ctls = q->q_ptr; 6600Sstevel@tonic-gate 661269Sericheng switch (mp->b_datap->db_type) { 662269Sericheng case M_IOCTL: 663269Sericheng drv_ioc(ctls, mp); 664269Sericheng break; 665269Sericheng default: 666269Sericheng freemsg(mp); 667269Sericheng break; 668269Sericheng } 669269Sericheng } 6700Sstevel@tonic-gate 671269Sericheng /* 672269Sericheng * Write-side service procedure. 673269Sericheng */ 674269Sericheng void 675269Sericheng drv_uw_srv(queue_t *q) 676269Sericheng { 677269Sericheng mblk_t *mp; 6780Sstevel@tonic-gate 679269Sericheng while (mp = getq(q)) 680269Sericheng drv_uw_put(q, mp); 6810Sstevel@tonic-gate } 6823147Sxc151355 6833147Sxc151355 /* 6843147Sxc151355 * Secure objects implementation 6853147Sxc151355 */ 6863147Sxc151355 6873147Sxc151355 /* ARGSUSED */ 6883147Sxc151355 static int 6893147Sxc151355 drv_secobj_ctor(void *buf, void *arg, int kmflag) 6903147Sxc151355 { 6913147Sxc151355 bzero(buf, sizeof (dld_secobj_t)); 6923147Sxc151355 return (0); 6933147Sxc151355 } 6943147Sxc151355 6953147Sxc151355 static void 6963147Sxc151355 drv_secobj_init(void) 6973147Sxc151355 { 6983147Sxc151355 rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL); 6993147Sxc151355 drv_secobj_cachep = kmem_cache_create("drv_secobj_cache", 7003147Sxc151355 sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL, 7013147Sxc151355 NULL, NULL, NULL, 0); 7023147Sxc151355 drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash", 7033147Sxc151355 SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, 7043147Sxc151355 mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); 7053147Sxc151355 } 7063147Sxc151355 7073147Sxc151355 static void 7083147Sxc151355 drv_secobj_fini(void) 7093147Sxc151355 { 7103147Sxc151355 mod_hash_destroy_hash(drv_secobj_hash); 7113147Sxc151355 kmem_cache_destroy(drv_secobj_cachep); 7123147Sxc151355 rw_destroy(&drv_secobj_lock); 7133147Sxc151355 } 7143147Sxc151355 7153147Sxc151355 static void 7163147Sxc151355 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp) 7173147Sxc151355 { 7183147Sxc151355 dld_ioc_secobj_set_t *ssp; 7193147Sxc151355 dld_secobj_t *sobjp, *objp; 7203147Sxc151355 int err = EINVAL; 7213147Sxc151355 queue_t *q = ctls->cs_wq; 7223147Sxc151355 7233147Sxc151355 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0) 7243147Sxc151355 goto failed; 7253147Sxc151355 7263147Sxc151355 ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr; 7273147Sxc151355 sobjp = &ssp->ss_obj; 7283147Sxc151355 729*4126Szf162725 if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP && 730*4126Szf162725 sobjp->so_class != DLD_SECOBJ_CLASS_WPA) 7313147Sxc151355 goto failed; 7323147Sxc151355 7333147Sxc151355 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' || 7343147Sxc151355 sobjp->so_len > DLD_SECOBJ_VAL_MAX) 7353147Sxc151355 goto failed; 7363147Sxc151355 7373147Sxc151355 rw_enter(&drv_secobj_lock, RW_WRITER); 7383147Sxc151355 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name, 7393147Sxc151355 (mod_hash_val_t *)&objp); 7403147Sxc151355 if (err == 0) { 7413147Sxc151355 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) { 7423147Sxc151355 err = EEXIST; 7433147Sxc151355 rw_exit(&drv_secobj_lock); 7443147Sxc151355 goto failed; 7453147Sxc151355 } 7463147Sxc151355 } else { 7473147Sxc151355 ASSERT(err == MH_ERR_NOTFOUND); 7483147Sxc151355 if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) { 7493147Sxc151355 err = ENOENT; 7503147Sxc151355 rw_exit(&drv_secobj_lock); 7513147Sxc151355 goto failed; 7523147Sxc151355 } 7533147Sxc151355 objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP); 7543147Sxc151355 (void) strlcpy(objp->so_name, sobjp->so_name, 7553147Sxc151355 DLD_SECOBJ_NAME_MAX); 7563147Sxc151355 7573147Sxc151355 err = mod_hash_insert(drv_secobj_hash, 7583147Sxc151355 (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp); 7593147Sxc151355 ASSERT(err == 0); 7603147Sxc151355 } 7613147Sxc151355 bcopy(sobjp->so_val, objp->so_val, sobjp->so_len); 7623147Sxc151355 objp->so_len = sobjp->so_len; 7633147Sxc151355 objp->so_class = sobjp->so_class; 7643147Sxc151355 rw_exit(&drv_secobj_lock); 7653147Sxc151355 miocack(q, mp, 0, 0); 7663147Sxc151355 return; 7673147Sxc151355 7683147Sxc151355 failed: 7693147Sxc151355 ASSERT(err != 0); 7703147Sxc151355 miocnak(q, mp, 0, err); 7713147Sxc151355 } 7723147Sxc151355 7733147Sxc151355 typedef struct dld_secobj_state { 7743147Sxc151355 uint_t ss_free; 7753147Sxc151355 uint_t ss_count; 7763147Sxc151355 int ss_rc; 7773147Sxc151355 dld_secobj_t *ss_objp; 7783147Sxc151355 } dld_secobj_state_t; 7793147Sxc151355 7803147Sxc151355 /* ARGSUSED */ 7813147Sxc151355 static uint_t 7823147Sxc151355 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg) 7833147Sxc151355 { 7843147Sxc151355 dld_secobj_state_t *statep = arg; 7853147Sxc151355 dld_secobj_t *sobjp = (dld_secobj_t *)val; 7863147Sxc151355 7873147Sxc151355 if (statep->ss_free < sizeof (dld_secobj_t)) { 7883147Sxc151355 statep->ss_rc = ENOSPC; 7893147Sxc151355 return (MH_WALK_TERMINATE); 7903147Sxc151355 } 7913147Sxc151355 bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t)); 7923147Sxc151355 statep->ss_objp++; 7933147Sxc151355 statep->ss_free -= sizeof (dld_secobj_t); 7943147Sxc151355 statep->ss_count++; 7953147Sxc151355 return (MH_WALK_CONTINUE); 7963147Sxc151355 } 7973147Sxc151355 7983147Sxc151355 static void 7993147Sxc151355 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp) 8003147Sxc151355 { 8013147Sxc151355 dld_ioc_secobj_get_t *sgp; 8023147Sxc151355 dld_secobj_t *sobjp, *objp; 8033147Sxc151355 int err = EINVAL; 8043147Sxc151355 uint_t extra = 0; 8053147Sxc151355 queue_t *q = ctls->cs_wq; 8063147Sxc151355 mblk_t *bp; 8073147Sxc151355 8083147Sxc151355 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0) 8093147Sxc151355 goto failed; 8103147Sxc151355 8113147Sxc151355 if ((bp = msgpullup(mp->b_cont, -1)) == NULL) 8123147Sxc151355 goto failed; 8133147Sxc151355 8143147Sxc151355 freemsg(mp->b_cont); 8153147Sxc151355 mp->b_cont = bp; 8163147Sxc151355 sgp = (dld_ioc_secobj_get_t *)bp->b_rptr; 8173147Sxc151355 sobjp = &sgp->sg_obj; 8183147Sxc151355 8193147Sxc151355 if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 8203147Sxc151355 goto failed; 8213147Sxc151355 8223147Sxc151355 rw_enter(&drv_secobj_lock, RW_READER); 8233147Sxc151355 if (sobjp->so_name[0] != '\0') { 8243147Sxc151355 err = mod_hash_find(drv_secobj_hash, 8253147Sxc151355 (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp); 8263147Sxc151355 if (err != 0) { 8273147Sxc151355 ASSERT(err == MH_ERR_NOTFOUND); 8283147Sxc151355 err = ENOENT; 8293147Sxc151355 rw_exit(&drv_secobj_lock); 8303147Sxc151355 goto failed; 8313147Sxc151355 } 8323147Sxc151355 bcopy(objp->so_val, sobjp->so_val, objp->so_len); 8333147Sxc151355 sobjp->so_len = objp->so_len; 8343147Sxc151355 sobjp->so_class = objp->so_class; 8353147Sxc151355 sgp->sg_count = 1; 8363147Sxc151355 } else { 8373147Sxc151355 dld_secobj_state_t state; 8383147Sxc151355 8393147Sxc151355 state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t); 8403147Sxc151355 state.ss_count = 0; 8413147Sxc151355 state.ss_rc = 0; 8423147Sxc151355 state.ss_objp = (dld_secobj_t *)(sgp + 1); 8433147Sxc151355 mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state); 8443147Sxc151355 if (state.ss_rc != 0) { 8453147Sxc151355 err = state.ss_rc; 8463147Sxc151355 rw_exit(&drv_secobj_lock); 8473147Sxc151355 goto failed; 8483147Sxc151355 } 8493147Sxc151355 sgp->sg_count = state.ss_count; 8503147Sxc151355 extra = state.ss_count * sizeof (dld_secobj_t); 8513147Sxc151355 } 8523147Sxc151355 rw_exit(&drv_secobj_lock); 8533147Sxc151355 miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0); 8543147Sxc151355 return; 8553147Sxc151355 8563147Sxc151355 failed: 8573147Sxc151355 ASSERT(err != 0); 8583147Sxc151355 miocnak(q, mp, 0, err); 8593147Sxc151355 8603147Sxc151355 } 8613147Sxc151355 8623147Sxc151355 static void 8633147Sxc151355 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp) 8643147Sxc151355 { 8653147Sxc151355 dld_ioc_secobj_unset_t *sup; 8663147Sxc151355 dld_secobj_t *objp; 8673147Sxc151355 mod_hash_val_t val; 8683147Sxc151355 int err = EINVAL; 8693147Sxc151355 queue_t *q = ctls->cs_wq; 8703147Sxc151355 8713147Sxc151355 if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0) 8723147Sxc151355 goto failed; 8733147Sxc151355 8743147Sxc151355 sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr; 8753147Sxc151355 if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0') 8763147Sxc151355 goto failed; 8773147Sxc151355 8783147Sxc151355 rw_enter(&drv_secobj_lock, RW_WRITER); 8793147Sxc151355 err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 8803147Sxc151355 (mod_hash_val_t *)&objp); 8813147Sxc151355 if (err != 0) { 8823147Sxc151355 ASSERT(err == MH_ERR_NOTFOUND); 8833147Sxc151355 err = ENOENT; 8843147Sxc151355 rw_exit(&drv_secobj_lock); 8853147Sxc151355 goto failed; 8863147Sxc151355 } 8873147Sxc151355 err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name, 8883147Sxc151355 (mod_hash_val_t *)&val); 8893147Sxc151355 ASSERT(err == 0); 8903147Sxc151355 ASSERT(objp == (dld_secobj_t *)val); 8913147Sxc151355 8923147Sxc151355 kmem_cache_free(drv_secobj_cachep, objp); 8933147Sxc151355 rw_exit(&drv_secobj_lock); 8943147Sxc151355 miocack(q, mp, 0, 0); 8953147Sxc151355 return; 8963147Sxc151355 8973147Sxc151355 failed: 8983147Sxc151355 ASSERT(err != 0); 8993147Sxc151355 miocnak(q, mp, 0, err); 9003147Sxc151355 } 901