11ae08745Sheppo /* 21ae08745Sheppo * CDDL HEADER START 31ae08745Sheppo * 41ae08745Sheppo * The contents of this file are subject to the terms of the 51ae08745Sheppo * Common Development and Distribution License (the "License"). 61ae08745Sheppo * You may not use this file except in compliance with the License. 71ae08745Sheppo * 81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91ae08745Sheppo * or http://www.opensolaris.org/os/licensing. 101ae08745Sheppo * See the License for the specific language governing permissions 111ae08745Sheppo * and limitations under the License. 121ae08745Sheppo * 131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the 161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181ae08745Sheppo * 191ae08745Sheppo * CDDL HEADER END 201ae08745Sheppo */ 211ae08745Sheppo 221ae08745Sheppo /* 23*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 241ae08745Sheppo * Use is subject to license terms. 251ae08745Sheppo */ 261ae08745Sheppo 271ae08745Sheppo 281ae08745Sheppo #include <sys/types.h> 291ae08745Sheppo #include <sys/file.h> 301ae08745Sheppo #include <sys/errno.h> 311ae08745Sheppo #include <sys/uio.h> 321ae08745Sheppo #include <sys/open.h> 331ae08745Sheppo #include <sys/cred.h> 341ae08745Sheppo #include <sys/kmem.h> 351ae08745Sheppo #include <sys/conf.h> 361ae08745Sheppo #include <sys/cmn_err.h> 371ae08745Sheppo #include <sys/ksynch.h> 381ae08745Sheppo #include <sys/modctl.h> 391ae08745Sheppo #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */ 401ae08745Sheppo #include <sys/debug.h> 411ae08745Sheppo #include <sys/sysmacros.h> 421ae08745Sheppo #include <sys/types.h> 431ae08745Sheppo #include <sys/cred.h> 441ae08745Sheppo #include <sys/promif.h> 451ae08745Sheppo #include <sys/ddi.h> 461ae08745Sheppo #include <sys/sunddi.h> 471ae08745Sheppo #include <sys/cyclic.h> 481ae08745Sheppo #include <sys/note.h> 491ae08745Sheppo #include <sys/mach_descrip.h> 501ae08745Sheppo #include <sys/mdeg.h> 511ae08745Sheppo #include <sys/ldc.h> 521ae08745Sheppo #include <sys/vldc_impl.h> 531ae08745Sheppo 541ae08745Sheppo /* 551ae08745Sheppo * Function prototypes. 561ae08745Sheppo */ 571ae08745Sheppo 581ae08745Sheppo /* DDI entrypoints */ 591ae08745Sheppo static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 601ae08745Sheppo static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 611ae08745Sheppo static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred); 621ae08745Sheppo static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred); 631ae08745Sheppo static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 641ae08745Sheppo cred_t *credp, int *rvalp); 651ae08745Sheppo static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp); 661ae08745Sheppo static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp); 671ae08745Sheppo static int vldc_chpoll(dev_t dev, short events, int anyyet, 681ae08745Sheppo short *reventsp, struct pollhead **phpp); 691ae08745Sheppo 701ae08745Sheppo /* Internal functions */ 711ae08745Sheppo static uint_t i_vldc_cb(uint64_t event, caddr_t arg); 721ae08745Sheppo static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp); 731ae08745Sheppo static int i_vldc_mdeg_register(vldc_t *vldcp); 741ae08745Sheppo static int i_vldc_mdeg_unregister(vldc_t *vldcp); 751ae08745Sheppo static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node); 761ae08745Sheppo static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno); 771ae08745Sheppo static int i_vldc_close_port(vldc_t *vldcp, uint_t portno); 781ae08745Sheppo 791ae08745Sheppo /* soft state structure */ 801ae08745Sheppo static void *vldc_ssp; 811ae08745Sheppo 821ae08745Sheppo /* 831ae08745Sheppo * Matching criteria passed to the MDEG to register interest 841ae08745Sheppo * in changes to 'virtual-device-port' nodes identified by their 851ae08745Sheppo * 'id' property. 861ae08745Sheppo */ 871ae08745Sheppo static md_prop_match_t vport_prop_match[] = { 881ae08745Sheppo { MDET_PROP_VAL, "id" }, 891ae08745Sheppo { MDET_LIST_END, NULL } 901ae08745Sheppo }; 911ae08745Sheppo 921ae08745Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port", 931ae08745Sheppo vport_prop_match }; 941ae08745Sheppo 951ae08745Sheppo /* 961ae08745Sheppo * Specification of an MD node passed to the MDEG to filter any 971ae08745Sheppo * 'virtual-device-port' nodes that do not belong to the specified 981ae08745Sheppo * node. This template is copied for each vldc instance and filled 991ae08745Sheppo * in with the appropriate 'name' and 'cfg-handle' values before 1001ae08745Sheppo * being passed to the MDEG. 1011ae08745Sheppo */ 1021ae08745Sheppo static mdeg_prop_spec_t vldc_prop_template[] = { 1031ae08745Sheppo { MDET_PROP_STR, "name", NULL }, 1041ae08745Sheppo { MDET_PROP_VAL, "cfg-handle", NULL }, 1051ae08745Sheppo { MDET_LIST_END, NULL, NULL } 1061ae08745Sheppo }; 1071ae08745Sheppo 1081ae08745Sheppo #define VLDC_MDEG_PROP_NAME(specp) ((specp)[0].ps_str) 1091ae08745Sheppo #define VLDC_SET_MDEG_PROP_NAME(specp, name) ((specp)[0].ps_str = (name)) 1101ae08745Sheppo #define VLDC_SET_MDEG_PROP_INST(specp, inst) ((specp)[1].ps_val = (inst)) 1111ae08745Sheppo 1121ae08745Sheppo 1131ae08745Sheppo static struct cb_ops vldc_cb_ops = { 1141ae08745Sheppo vldc_open, /* open */ 1151ae08745Sheppo vldc_close, /* close */ 1161ae08745Sheppo nodev, /* strategy */ 1171ae08745Sheppo nodev, /* print */ 1181ae08745Sheppo nodev, /* dump */ 1191ae08745Sheppo vldc_read, /* read */ 1201ae08745Sheppo vldc_write, /* write */ 1211ae08745Sheppo vldc_ioctl, /* ioctl */ 1221ae08745Sheppo nodev, /* devmap */ 1231ae08745Sheppo nodev, /* mmap */ 1241ae08745Sheppo ddi_segmap, /* segmap */ 1251ae08745Sheppo vldc_chpoll, /* chpoll */ 1261ae08745Sheppo ddi_prop_op, /* prop_op */ 1271ae08745Sheppo NULL, /* stream */ 1281ae08745Sheppo D_NEW | D_MP /* flag */ 1291ae08745Sheppo }; 1301ae08745Sheppo 1311ae08745Sheppo static struct dev_ops vldc_ops = { 1321ae08745Sheppo DEVO_REV, /* rev */ 1331ae08745Sheppo 0, /* ref count */ 1341ae08745Sheppo ddi_getinfo_1to1, /* getinfo */ 1351ae08745Sheppo nulldev, /* identify */ 1361ae08745Sheppo nulldev, /* probe */ 1371ae08745Sheppo vldc_attach, /* attach */ 1381ae08745Sheppo vldc_detach, /* detach */ 1391ae08745Sheppo nodev, /* reset */ 1401ae08745Sheppo &vldc_cb_ops, /* cb_ops */ 14119397407SSherry Moore (struct bus_ops *)NULL, /* bus_ops */ 14219397407SSherry Moore NULL, /* power */ 14319397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 1441ae08745Sheppo }; 1451ae08745Sheppo 1461ae08745Sheppo extern struct mod_ops mod_driverops; 1471ae08745Sheppo 1481ae08745Sheppo static struct modldrv md = { 1491ae08745Sheppo &mod_driverops, /* Type - it is a driver */ 15019397407SSherry Moore "sun4v Virtual LDC Driver", /* Name of the module */ 1511ae08745Sheppo &vldc_ops, /* driver specific ops */ 1521ae08745Sheppo }; 1531ae08745Sheppo 1541ae08745Sheppo static struct modlinkage ml = { 1551ae08745Sheppo MODREV_1, 1561ae08745Sheppo &md, 1571ae08745Sheppo NULL 1581ae08745Sheppo }; 1591ae08745Sheppo 1601ae08745Sheppo /* maximum MTU and cookie size tunables */ 1611ae08745Sheppo uint32_t vldc_max_mtu = VLDC_MAX_MTU; 1621ae08745Sheppo uint64_t vldc_max_cookie = VLDC_MAX_COOKIE; 1631ae08745Sheppo 164cb112a14Slm66018 /* 1654d39be2bSsg70180 * when ldc_close() returns EAGAIN, it is retried with a wait 1664d39be2bSsg70180 * of 'vldc_close_delay' between each retry. 167cb112a14Slm66018 */ 1684d39be2bSsg70180 static clock_t vldc_close_delay = VLDC_CLOSE_DELAY; 1691ae08745Sheppo 1701ae08745Sheppo #ifdef DEBUG 1711ae08745Sheppo 1721ae08745Sheppo /* 1731ae08745Sheppo * Print debug messages 1741ae08745Sheppo * 1751ae08745Sheppo * set vldcdbg to 0x7 to enable all messages 1761ae08745Sheppo * 1771ae08745Sheppo * 0x4 - Warnings 1781ae08745Sheppo * 0x2 - All debug messages (most verbose) 1791ae08745Sheppo * 0x1 - Minimal debug messages 1801ae08745Sheppo */ 1811ae08745Sheppo 1821ae08745Sheppo int vldcdbg = 0x0; 1831ae08745Sheppo 1841ae08745Sheppo static void 1851ae08745Sheppo vldcdebug(const char *fmt, ...) 1861ae08745Sheppo { 1871ae08745Sheppo char buf[512]; 1881ae08745Sheppo va_list ap; 1891ae08745Sheppo 1901ae08745Sheppo va_start(ap, fmt); 1911ae08745Sheppo (void) vsnprintf(buf, sizeof (buf), fmt, ap); 1921ae08745Sheppo va_end(ap); 1931ae08745Sheppo 1941ae08745Sheppo cmn_err(CE_CONT, "?%s", buf); 1951ae08745Sheppo } 1961ae08745Sheppo 1971ae08745Sheppo #define D1 if (vldcdbg & 0x01) vldcdebug 1981ae08745Sheppo #define D2 if (vldcdbg & 0x02) vldcdebug 1991ae08745Sheppo #define DWARN if (vldcdbg & 0x04) vldcdebug 2001ae08745Sheppo 2011ae08745Sheppo #else /* not DEBUG */ 2021ae08745Sheppo 2031ae08745Sheppo #define D1 if (0) printf 2041ae08745Sheppo #define D2 if (0) printf 2051ae08745Sheppo #define DWARN if (0) printf 2061ae08745Sheppo 2071ae08745Sheppo #endif /* not DEBUG */ 2081ae08745Sheppo 2091ae08745Sheppo 2101ae08745Sheppo /* _init(9E): initialize the loadable module */ 2111ae08745Sheppo int 2121ae08745Sheppo _init(void) 2131ae08745Sheppo { 2141ae08745Sheppo int error; 2151ae08745Sheppo 2161ae08745Sheppo /* init the soft state structure */ 2171ae08745Sheppo error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1); 2181ae08745Sheppo if (error != 0) { 2191ae08745Sheppo return (error); 2201ae08745Sheppo } 2211ae08745Sheppo 2221ae08745Sheppo /* Link the driver into the system */ 2231ae08745Sheppo error = mod_install(&ml); 2241ae08745Sheppo 2251ae08745Sheppo return (error); 2261ae08745Sheppo } 2271ae08745Sheppo 2281ae08745Sheppo /* _info(9E): return information about the loadable module */ 2291ae08745Sheppo int 2301ae08745Sheppo _info(struct modinfo *modinfop) 2311ae08745Sheppo { 2321ae08745Sheppo /* Report status of the dynamically loadable driver module */ 2331ae08745Sheppo return (mod_info(&ml, modinfop)); 2341ae08745Sheppo } 2351ae08745Sheppo 2361ae08745Sheppo /* _fini(9E): prepare the module for unloading. */ 2371ae08745Sheppo int 2381ae08745Sheppo _fini(void) 2391ae08745Sheppo { 2401ae08745Sheppo int error; 2411ae08745Sheppo 2421ae08745Sheppo /* Unlink the driver module from the system */ 2431ae08745Sheppo if ((error = mod_remove(&ml)) == 0) { 2441ae08745Sheppo /* 2451ae08745Sheppo * We have successfully "removed" the driver. 2461ae08745Sheppo * destroy soft state 2471ae08745Sheppo */ 2481ae08745Sheppo ddi_soft_state_fini(&vldc_ssp); 2491ae08745Sheppo } 2501ae08745Sheppo 2511ae08745Sheppo return (error); 2521ae08745Sheppo } 2531ae08745Sheppo 2541ae08745Sheppo /* ldc callback */ 2551ae08745Sheppo static uint_t 2561ae08745Sheppo i_vldc_cb(uint64_t event, caddr_t arg) 2571ae08745Sheppo { 258a8ea4edeSnarayan int rv; 2591ae08745Sheppo vldc_port_t *vport = (vldc_port_t *)arg; 260a8ea4edeSnarayan ldc_status_t old_status; 2611ae08745Sheppo short pollevents = 0; 2621ae08745Sheppo 263cb112a14Slm66018 ASSERT(vport != NULL); 264cb112a14Slm66018 ASSERT(vport->minorp != NULL); 265cb112a14Slm66018 2663af08d82Slm66018 D1("i_vldc_cb: vldc@%d:%d callback invoked, channel=0x%lx, " 2673af08d82Slm66018 "event=0x%lx\n", vport->inst, vport->number, vport->ldc_id, event); 2681ae08745Sheppo 269cb112a14Slm66018 /* ensure the port can't be destroyed while we are handling the cb */ 270cb112a14Slm66018 mutex_enter(&vport->minorp->lock); 271cb112a14Slm66018 2724d39be2bSsg70180 if (vport->status == VLDC_PORT_CLOSED) { 2734d39be2bSsg70180 return (LDC_SUCCESS); 2744d39be2bSsg70180 } 2754d39be2bSsg70180 276a8ea4edeSnarayan old_status = vport->ldc_status; 277a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status); 278a8ea4edeSnarayan if (rv != 0) { 279a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d could not get ldc status, " 280a8ea4edeSnarayan "rv=%d\n", vport->inst, vport->number, rv); 281cb112a14Slm66018 mutex_exit(&vport->minorp->lock); 282a8ea4edeSnarayan return (LDC_SUCCESS); 283a8ea4edeSnarayan } 284a8ea4edeSnarayan 2851ae08745Sheppo if (event & LDC_EVT_UP) { 2861ae08745Sheppo pollevents |= POLLOUT; 2871ae08745Sheppo vport->hanged_up = B_FALSE; 2881ae08745Sheppo 2891ae08745Sheppo } else if (event & LDC_EVT_RESET) { 2903af08d82Slm66018 /* 291a8ea4edeSnarayan * Mark the port in reset, if it is not CLOSED and 292a8ea4edeSnarayan * the channel was previously in LDC_UP state. This 293a8ea4edeSnarayan * implies that the port cannot be used until it has 294a8ea4edeSnarayan * been closed and reopened. 2953af08d82Slm66018 */ 2964d39be2bSsg70180 if (old_status == LDC_UP) { 2973af08d82Slm66018 vport->status = VLDC_PORT_RESET; 298a8ea4edeSnarayan vport->hanged_up = B_TRUE; 299a8ea4edeSnarayan pollevents = POLLHUP; 300a8ea4edeSnarayan } else { 301a8ea4edeSnarayan rv = ldc_up(vport->ldc_handle); 302a8ea4edeSnarayan if (rv) { 303a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d cannot bring " 304a8ea4edeSnarayan "channel UP rv=%d\n", vport->inst, 305a8ea4edeSnarayan vport->number, rv); 306cb112a14Slm66018 mutex_exit(&vport->minorp->lock); 3073af08d82Slm66018 return (LDC_SUCCESS); 308a8ea4edeSnarayan } 309a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status); 310a8ea4edeSnarayan if (rv != 0) { 311a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d could not get " 312a8ea4edeSnarayan "ldc status, rv=%d\n", vport->inst, 313a8ea4edeSnarayan vport->number, rv); 314cb112a14Slm66018 mutex_exit(&vport->minorp->lock); 315a8ea4edeSnarayan return (LDC_SUCCESS); 316a8ea4edeSnarayan } 317a8ea4edeSnarayan if (vport->ldc_status == LDC_UP) { 318a8ea4edeSnarayan pollevents |= POLLOUT; 319a8ea4edeSnarayan vport->hanged_up = B_FALSE; 320a8ea4edeSnarayan } 321a8ea4edeSnarayan } 3223af08d82Slm66018 3233af08d82Slm66018 } else if (event & LDC_EVT_DOWN) { 3243af08d82Slm66018 /* 325a8ea4edeSnarayan * The other side went away - mark port in RESET state 3263af08d82Slm66018 */ 327a8ea4edeSnarayan vport->status = VLDC_PORT_RESET; 3283af08d82Slm66018 vport->hanged_up = B_TRUE; 3293af08d82Slm66018 pollevents = POLLHUP; 3301ae08745Sheppo } 3311ae08745Sheppo 3321ae08745Sheppo if (event & LDC_EVT_READ) 3331ae08745Sheppo pollevents |= POLLIN; 3341ae08745Sheppo 335cb112a14Slm66018 mutex_exit(&vport->minorp->lock); 336cb112a14Slm66018 3371ae08745Sheppo if (pollevents != 0) { 3381ae08745Sheppo D1("i_vldc_cb: port@%d pollwakeup=0x%x\n", 3391ae08745Sheppo vport->number, pollevents); 3401ae08745Sheppo pollwakeup(&vport->poll, pollevents); 3411ae08745Sheppo } 3421ae08745Sheppo 3431ae08745Sheppo return (LDC_SUCCESS); 3441ae08745Sheppo } 3451ae08745Sheppo 3461ae08745Sheppo /* mdeg callback */ 3471ae08745Sheppo static int 3481ae08745Sheppo i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp) 3491ae08745Sheppo { 3501ae08745Sheppo vldc_t *vldcp; 3511ae08745Sheppo int idx; 3521ae08745Sheppo uint64_t portno; 3531ae08745Sheppo int rv; 3541ae08745Sheppo md_t *mdp; 3551ae08745Sheppo mde_cookie_t node; 3561ae08745Sheppo 3571ae08745Sheppo if (resp == NULL) { 3581ae08745Sheppo D1("i_vldc_mdeg_cb: no result returned\n"); 3591ae08745Sheppo return (MDEG_FAILURE); 3601ae08745Sheppo } 3611ae08745Sheppo 3621ae08745Sheppo vldcp = (vldc_t *)cb_argp; 3631ae08745Sheppo 3641ae08745Sheppo mutex_enter(&vldcp->lock); 3651ae08745Sheppo if (vldcp->detaching == B_TRUE) { 3661ae08745Sheppo D1("i_vldc_mdeg_cb: detach in progress\n"); 3671ae08745Sheppo mutex_exit(&vldcp->lock); 3681ae08745Sheppo return (MDEG_FAILURE); 3691ae08745Sheppo } 3701ae08745Sheppo 3711ae08745Sheppo D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n", 3721ae08745Sheppo resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem); 3731ae08745Sheppo 3741ae08745Sheppo /* process added ports */ 3751ae08745Sheppo for (idx = 0; idx < resp->added.nelem; idx++) { 3761ae08745Sheppo mdp = resp->added.mdp; 3771ae08745Sheppo node = resp->added.mdep[idx]; 3781ae08745Sheppo 3791ae08745Sheppo D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node); 3801ae08745Sheppo 3811ae08745Sheppo /* attempt to add a port */ 3821ae08745Sheppo if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) { 3831ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, " 3841ae08745Sheppo "err = %d", rv); 3851ae08745Sheppo } 3861ae08745Sheppo } 3871ae08745Sheppo 3881ae08745Sheppo /* process removed ports */ 3891ae08745Sheppo for (idx = 0; idx < resp->removed.nelem; idx++) { 3901ae08745Sheppo mdp = resp->removed.mdp; 3911ae08745Sheppo node = resp->removed.mdep[idx]; 3921ae08745Sheppo 3931ae08745Sheppo D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node); 3941ae08745Sheppo 3951ae08745Sheppo /* read in the port's id property */ 3961ae08745Sheppo if (md_get_prop_val(mdp, node, "id", &portno)) { 3971ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of " 3981ae08745Sheppo "removed list has no 'id' property", node); 3991ae08745Sheppo continue; 4001ae08745Sheppo } 4011ae08745Sheppo 4021ae08745Sheppo /* attempt to remove a port */ 4031ae08745Sheppo if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) { 4041ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove " 4051ae08745Sheppo "port %lu, err %d", portno, rv); 4061ae08745Sheppo } 4071ae08745Sheppo } 4081ae08745Sheppo 4091ae08745Sheppo /* 4101ae08745Sheppo * Currently no support for updating already active ports. So, ignore 4111ae08745Sheppo * the match_curr and match_prev arrays for now. 4121ae08745Sheppo */ 4131ae08745Sheppo 4141ae08745Sheppo mutex_exit(&vldcp->lock); 4151ae08745Sheppo 4161ae08745Sheppo return (MDEG_SUCCESS); 4171ae08745Sheppo } 4181ae08745Sheppo 4191ae08745Sheppo /* register callback to mdeg */ 4201ae08745Sheppo static int 4211ae08745Sheppo i_vldc_mdeg_register(vldc_t *vldcp) 4221ae08745Sheppo { 4231ae08745Sheppo mdeg_prop_spec_t *pspecp; 4241ae08745Sheppo mdeg_node_spec_t *inst_specp; 4251ae08745Sheppo mdeg_handle_t mdeg_hdl; 4261ae08745Sheppo size_t templatesz; 4271ae08745Sheppo int inst; 4281ae08745Sheppo char *name; 4291ae08745Sheppo size_t namesz; 4301ae08745Sheppo char *nameprop; 4311ae08745Sheppo int rv; 4321ae08745Sheppo 4331ae08745Sheppo /* get the unique vldc instance assigned by the LDom manager */ 4341ae08745Sheppo inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip, 4351ae08745Sheppo DDI_PROP_DONTPASS, "reg", -1); 4361ae08745Sheppo if (inst == -1) { 4371ae08745Sheppo cmn_err(CE_NOTE, "?vldc%d has no 'reg' property", 4381ae08745Sheppo ddi_get_instance(vldcp->dip)); 4391ae08745Sheppo return (DDI_FAILURE); 4401ae08745Sheppo } 4411ae08745Sheppo 4421ae08745Sheppo /* get the name of the vldc instance */ 4431ae08745Sheppo rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip, 4441ae08745Sheppo DDI_PROP_DONTPASS, "name", &nameprop); 4451ae08745Sheppo if (rv != DDI_PROP_SUCCESS) { 4461ae08745Sheppo cmn_err(CE_NOTE, "?vldc%d has no 'name' property", 4471ae08745Sheppo ddi_get_instance(vldcp->dip)); 4481ae08745Sheppo return (DDI_FAILURE); 4491ae08745Sheppo } 4501ae08745Sheppo 4511ae08745Sheppo D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst); 4521ae08745Sheppo 4531ae08745Sheppo /* 4541ae08745Sheppo * Allocate and initialize a per-instance copy 4551ae08745Sheppo * of the global property spec array that will 4561ae08745Sheppo * uniquely identify this vldc instance. 4571ae08745Sheppo */ 4581ae08745Sheppo templatesz = sizeof (vldc_prop_template); 4591ae08745Sheppo pspecp = kmem_alloc(templatesz, KM_SLEEP); 4601ae08745Sheppo 4611ae08745Sheppo bcopy(vldc_prop_template, pspecp, templatesz); 4621ae08745Sheppo 4631ae08745Sheppo /* copy in the name property */ 4641ae08745Sheppo namesz = strlen(nameprop) + 1; 4651ae08745Sheppo name = kmem_alloc(namesz, KM_SLEEP); 4661ae08745Sheppo 4671ae08745Sheppo bcopy(nameprop, name, namesz); 4681ae08745Sheppo VLDC_SET_MDEG_PROP_NAME(pspecp, name); 469d10e4ef2Snarayan ddi_prop_free(nameprop); 4701ae08745Sheppo 4711ae08745Sheppo /* copy in the instance property */ 4721ae08745Sheppo VLDC_SET_MDEG_PROP_INST(pspecp, inst); 4731ae08745Sheppo 4741ae08745Sheppo /* initialize the complete prop spec structure */ 4751ae08745Sheppo inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP); 4761ae08745Sheppo inst_specp->namep = "virtual-device"; 4771ae08745Sheppo inst_specp->specp = pspecp; 4781ae08745Sheppo 4791ae08745Sheppo /* perform the registration */ 4801ae08745Sheppo rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb, 4811ae08745Sheppo vldcp, &mdeg_hdl); 4821ae08745Sheppo 4831ae08745Sheppo if (rv != MDEG_SUCCESS) { 4841ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register " 4851ae08745Sheppo "failed, err = %d", rv); 4861ae08745Sheppo kmem_free(name, namesz); 4871ae08745Sheppo kmem_free(pspecp, templatesz); 4881ae08745Sheppo kmem_free(inst_specp, sizeof (mdeg_node_spec_t)); 4891ae08745Sheppo return (DDI_FAILURE); 4901ae08745Sheppo } 4911ae08745Sheppo 4921ae08745Sheppo /* save off data that will be needed later */ 4931ae08745Sheppo vldcp->inst_spec = inst_specp; 4941ae08745Sheppo vldcp->mdeg_hdl = mdeg_hdl; 4951ae08745Sheppo 4961ae08745Sheppo return (DDI_SUCCESS); 4971ae08745Sheppo } 4981ae08745Sheppo 4991ae08745Sheppo /* unregister callback from mdeg */ 5001ae08745Sheppo static int 5011ae08745Sheppo i_vldc_mdeg_unregister(vldc_t *vldcp) 5021ae08745Sheppo { 5031ae08745Sheppo char *name; 5041ae08745Sheppo int rv; 5051ae08745Sheppo 5061ae08745Sheppo D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl); 5071ae08745Sheppo 5081ae08745Sheppo rv = mdeg_unregister(vldcp->mdeg_hdl); 5091ae08745Sheppo if (rv != MDEG_SUCCESS) { 5101ae08745Sheppo return (rv); 5111ae08745Sheppo } 5121ae08745Sheppo 5131ae08745Sheppo /* 5141ae08745Sheppo * Clean up cached MDEG data 5151ae08745Sheppo */ 5161ae08745Sheppo name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp); 5171ae08745Sheppo if (name != NULL) { 5181ae08745Sheppo kmem_free(name, strlen(name) + 1); 5191ae08745Sheppo } 5201ae08745Sheppo kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template)); 5211ae08745Sheppo vldcp->inst_spec->specp = NULL; 5221ae08745Sheppo 5231ae08745Sheppo kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t)); 5241ae08745Sheppo vldcp->inst_spec = NULL; 5251ae08745Sheppo 5261ae08745Sheppo return (MDEG_SUCCESS); 5271ae08745Sheppo } 5281ae08745Sheppo 5291ae08745Sheppo static int 5301ae08745Sheppo i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id) 5311ae08745Sheppo { 5321ae08745Sheppo int num_nodes, nchan; 5331ae08745Sheppo size_t listsz; 5341ae08745Sheppo mde_cookie_t *listp; 5351ae08745Sheppo 5361ae08745Sheppo /* 5371ae08745Sheppo * Find the channel-endpoint node(s) (which should be under this 5381ae08745Sheppo * port node) which contain the channel id(s). 5391ae08745Sheppo */ 5401ae08745Sheppo if ((num_nodes = md_node_count(mdp)) <= 0) { 5411ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of " 5421ae08745Sheppo "channel-endpoint nodes found (%d)", num_nodes); 5431ae08745Sheppo return (-1); 5441ae08745Sheppo } 5451ae08745Sheppo 5461ae08745Sheppo /* allocate space for node list */ 5471ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 5481ae08745Sheppo listp = kmem_alloc(listsz, KM_SLEEP); 5491ae08745Sheppo 5501ae08745Sheppo nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"), 5511ae08745Sheppo md_find_name(mdp, "fwd"), listp); 5521ae08745Sheppo 5531ae08745Sheppo if (nchan <= 0) { 5541ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint" 5551ae08745Sheppo " nodes found"); 5561ae08745Sheppo kmem_free(listp, listsz); 5571ae08745Sheppo return (-1); 5581ae08745Sheppo } 5591ae08745Sheppo 5601ae08745Sheppo D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan); 5611ae08745Sheppo 5621ae08745Sheppo /* use property from first node found */ 5631ae08745Sheppo if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) { 5641ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint " 5651ae08745Sheppo "has no 'id' property"); 5661ae08745Sheppo kmem_free(listp, listsz); 5671ae08745Sheppo return (-1); 5681ae08745Sheppo } 5691ae08745Sheppo 5701ae08745Sheppo kmem_free(listp, listsz); 5711ae08745Sheppo 5721ae08745Sheppo return (0); 5731ae08745Sheppo } 5741ae08745Sheppo 5751ae08745Sheppo /* add a vldc port */ 5761ae08745Sheppo static int 5771ae08745Sheppo i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node) 5781ae08745Sheppo { 5791ae08745Sheppo vldc_port_t *vport; 5801ae08745Sheppo char *sname; 5811ae08745Sheppo uint64_t portno; 5821ae08745Sheppo int vldc_inst; 5831ae08745Sheppo minor_t minor; 5841ae08745Sheppo int minor_idx; 5851ae08745Sheppo boolean_t new_minor; 5861ae08745Sheppo int rv; 5871ae08745Sheppo 588cb112a14Slm66018 ASSERT(MUTEX_HELD(&vldcp->lock)); 589cb112a14Slm66018 5901ae08745Sheppo /* read in the port's id property */ 5911ae08745Sheppo if (md_get_prop_val(mdp, node, "id", &portno)) { 5921ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added " 5931ae08745Sheppo "list has no 'id' property", node); 5941ae08745Sheppo return (MDEG_FAILURE); 5951ae08745Sheppo } 5961ae08745Sheppo 5971ae08745Sheppo if (portno >= VLDC_MAX_PORTS) { 5981ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) " 5991ae08745Sheppo "larger than maximum supported number of ports", portno); 6001ae08745Sheppo return (MDEG_FAILURE); 6011ae08745Sheppo } 6021ae08745Sheppo 6031ae08745Sheppo vport = &(vldcp->port[portno]); 6041ae08745Sheppo 6051ae08745Sheppo if (vport->minorp != NULL) { 6061ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)" 6071ae08745Sheppo " which is already bound", portno); 6081ae08745Sheppo return (MDEG_FAILURE); 6091ae08745Sheppo } 6101ae08745Sheppo 6111ae08745Sheppo vport->number = portno; 6121ae08745Sheppo 6131ae08745Sheppo /* get all channels for this device (currently only one) */ 6141ae08745Sheppo if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) { 6151ae08745Sheppo return (MDEG_FAILURE); 6161ae08745Sheppo } 6171ae08745Sheppo 6181ae08745Sheppo /* set the default MTU */ 619d1a9c4c1Sjm22469 vport->mtu = VLDC_DEFAULT_MTU; 6201ae08745Sheppo 6211ae08745Sheppo /* get the service being exported by this port */ 6221ae08745Sheppo if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) { 6231ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no " 6241ae08745Sheppo "'vldc-svc-name' property"); 6251ae08745Sheppo return (MDEG_FAILURE); 6261ae08745Sheppo } 6271ae08745Sheppo 6281ae08745Sheppo /* minor number look up */ 6291ae08745Sheppo for (minor_idx = 0; minor_idx < vldcp->minors_assigned; 6301ae08745Sheppo minor_idx++) { 6311ae08745Sheppo if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) { 6321ae08745Sheppo /* found previously assigned minor number */ 6331ae08745Sheppo break; 6341ae08745Sheppo } 6351ae08745Sheppo } 6361ae08745Sheppo 6371ae08745Sheppo new_minor = B_FALSE; 6381ae08745Sheppo if (minor_idx == vldcp->minors_assigned) { 6391ae08745Sheppo /* end of lookup - assign new minor number */ 6401ae08745Sheppo if (vldcp->minors_assigned == VLDC_MAX_MINORS) { 6411ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor " 6421ae08745Sheppo "nodes (%d)", minor_idx); 6431ae08745Sheppo return (MDEG_FAILURE); 6441ae08745Sheppo } 6451ae08745Sheppo 6461ae08745Sheppo (void) strlcpy(vldcp->minor_tbl[minor_idx].sname, 6471ae08745Sheppo sname, MAXPATHLEN); 6481ae08745Sheppo 6491ae08745Sheppo vldcp->minors_assigned++; 6501ae08745Sheppo new_minor = B_TRUE; 6511ae08745Sheppo } 6521ae08745Sheppo 653cb112a14Slm66018 if (vldcp->minor_tbl[minor_idx].portno != VLDC_INVALID_PORTNO) { 654cb112a14Slm66018 cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)" 655cb112a14Slm66018 " which has a minor number in use by port (%u)", 656cb112a14Slm66018 portno, vldcp->minor_tbl[minor_idx].portno); 657cb112a14Slm66018 return (MDEG_FAILURE); 658cb112a14Slm66018 } 6591ae08745Sheppo 6603af08d82Slm66018 vldc_inst = ddi_get_instance(vldcp->dip); 6613af08d82Slm66018 6623af08d82Slm66018 vport->inst = vldc_inst; 6631ae08745Sheppo vport->minorp = &vldcp->minor_tbl[minor_idx]; 6641ae08745Sheppo vldcp->minor_tbl[minor_idx].portno = portno; 6651ae08745Sheppo vldcp->minor_tbl[minor_idx].in_use = 0; 6661ae08745Sheppo 6673af08d82Slm66018 D1("i_vldc_add_port: vldc@%d:%d mtu=%d, ldc=%ld, service=%s\n", 6683af08d82Slm66018 vport->inst, vport->number, vport->mtu, vport->ldc_id, sname); 6691ae08745Sheppo 6701ae08745Sheppo /* 6711ae08745Sheppo * Create a minor node. The minor number is 6721ae08745Sheppo * (vldc_inst << VLDC_INST_SHIFT) | minor_idx 6731ae08745Sheppo */ 6741ae08745Sheppo minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx); 6751ae08745Sheppo 6761ae08745Sheppo rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR, 6771ae08745Sheppo minor, DDI_NT_SERIAL, 0); 6781ae08745Sheppo 6791ae08745Sheppo if (rv != DDI_SUCCESS) { 6801ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor" 6811ae08745Sheppo "node (%u), err = %d", minor, rv); 6821ae08745Sheppo vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO; 6831ae08745Sheppo if (new_minor) { 6841ae08745Sheppo vldcp->minors_assigned--; 6851ae08745Sheppo } 6861ae08745Sheppo return (MDEG_FAILURE); 6871ae08745Sheppo } 6881ae08745Sheppo 6891ae08745Sheppo /* 6901ae08745Sheppo * The port is now bound to a minor node and is initially in the 6911ae08745Sheppo * closed state. 6921ae08745Sheppo */ 6931ae08745Sheppo vport->status = VLDC_PORT_CLOSED; 6941ae08745Sheppo 6951ae08745Sheppo D1("i_vldc_add_port: port %lu initialized\n", portno); 6961ae08745Sheppo 6971ae08745Sheppo return (MDEG_SUCCESS); 6981ae08745Sheppo } 6991ae08745Sheppo 7001ae08745Sheppo /* remove a vldc port */ 7011ae08745Sheppo static int 7021ae08745Sheppo i_vldc_remove_port(vldc_t *vldcp, uint_t portno) 7031ae08745Sheppo { 7041ae08745Sheppo vldc_port_t *vport; 7051ae08745Sheppo vldc_minor_t *vminor; 7061ae08745Sheppo 707cb112a14Slm66018 ASSERT(vldcp != NULL); 708cb112a14Slm66018 ASSERT(MUTEX_HELD(&vldcp->lock)); 709cb112a14Slm66018 7101ae08745Sheppo vport = &(vldcp->port[portno]); 7111ae08745Sheppo vminor = vport->minorp; 7121ae08745Sheppo if (vminor == NULL) { 7131ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a " 7141ae08745Sheppo "port (%u) which is not bound", portno); 7151ae08745Sheppo return (MDEG_FAILURE); 7161ae08745Sheppo } 7171ae08745Sheppo 7181ae08745Sheppo /* 7191ae08745Sheppo * Make sure that all new attempts to open or use the minor node 7201ae08745Sheppo * associated with the port will fail. 7211ae08745Sheppo */ 7221ae08745Sheppo mutex_enter(&vminor->lock); 7231ae08745Sheppo vminor->portno = VLDC_INVALID_PORTNO; 7241ae08745Sheppo mutex_exit(&vminor->lock); 7251ae08745Sheppo 7261ae08745Sheppo /* send hangup to anyone polling */ 7271ae08745Sheppo pollwakeup(&vport->poll, POLLHUP); 7281ae08745Sheppo 7291ae08745Sheppo /* Now wait for all current users of the minor node to finish. */ 7301ae08745Sheppo mutex_enter(&vminor->lock); 7311ae08745Sheppo while (vminor->in_use > 0) { 7321ae08745Sheppo cv_wait(&vminor->cv, &vminor->lock); 7331ae08745Sheppo } 7341ae08745Sheppo 7353af08d82Slm66018 if (vport->status != VLDC_PORT_CLOSED) { 7361ae08745Sheppo /* close the port before it is torn down */ 7371ae08745Sheppo (void) i_vldc_close_port(vldcp, portno); 7381ae08745Sheppo } 7391ae08745Sheppo 7401ae08745Sheppo /* remove minor node */ 7411ae08745Sheppo ddi_remove_minor_node(vldcp->dip, vport->minorp->sname); 7421ae08745Sheppo vport->minorp = NULL; 7431ae08745Sheppo 7441ae08745Sheppo mutex_exit(&vminor->lock); 7451ae08745Sheppo 7461ae08745Sheppo D1("i_vldc_remove_port: removed vldc port %u\n", portno); 7471ae08745Sheppo 7481ae08745Sheppo return (MDEG_SUCCESS); 7491ae08745Sheppo } 7501ae08745Sheppo 751cb112a14Slm66018 /* 752cb112a14Slm66018 * Close and destroy the ldc channel associated with the port 'vport' 753cb112a14Slm66018 * 754cb112a14Slm66018 * NOTE It may not be possible close and destroy the channel if resources 755cb112a14Slm66018 * are still in use so the fucntion may exit before all the teardown 756cb112a14Slm66018 * operations are completed and would have to be called again by the 757cb112a14Slm66018 * vldc framework. 758cb112a14Slm66018 * 759cb112a14Slm66018 * This function needs to be able to handle the case where it is called 760cb112a14Slm66018 * more than once and has to pick up from where it left off. 761cb112a14Slm66018 */ 7621ae08745Sheppo static int 7631ae08745Sheppo i_vldc_ldc_close(vldc_port_t *vport) 7641ae08745Sheppo { 765cb112a14Slm66018 int err = 0; 7661ae08745Sheppo 767cb112a14Slm66018 ASSERT(MUTEX_HELD(&vport->minorp->lock)); 768cb112a14Slm66018 769cb112a14Slm66018 /* 770cb112a14Slm66018 * If ldc_close() succeeded or if the channel was already closed[*] 771cb112a14Slm66018 * (possibly by a previously unsuccessful call to this function) 772cb112a14Slm66018 * we keep going and try to teardown the rest of the LDC state, 773cb112a14Slm66018 * otherwise we bail out. 774cb112a14Slm66018 * 775cb112a14Slm66018 * [*] indicated by ldc_close() returning a value of EFAULT 776cb112a14Slm66018 */ 7774d39be2bSsg70180 err = ldc_close(vport->ldc_handle); 778cb112a14Slm66018 if ((err != 0) && (err != EFAULT)) 779cb112a14Slm66018 return (err); 780cb112a14Slm66018 7811ae08745Sheppo err = ldc_unreg_callback(vport->ldc_handle); 782cb112a14Slm66018 if (err != 0) 783cb112a14Slm66018 return (err); 784cb112a14Slm66018 7851ae08745Sheppo err = ldc_fini(vport->ldc_handle); 786cb112a14Slm66018 if (err != 0) 787cb112a14Slm66018 return (err); 7881ae08745Sheppo 7893af08d82Slm66018 vport->status = VLDC_PORT_OPEN; 7903af08d82Slm66018 791cb112a14Slm66018 return (0); 7921ae08745Sheppo } 7931ae08745Sheppo 7941ae08745Sheppo /* close a vldc port */ 7951ae08745Sheppo static int 7961ae08745Sheppo i_vldc_close_port(vldc_t *vldcp, uint_t portno) 7971ae08745Sheppo { 7981ae08745Sheppo vldc_port_t *vport; 7994d39be2bSsg70180 vldc_minor_t *vminor; 8003af08d82Slm66018 int rv = DDI_SUCCESS; 8011ae08745Sheppo 8021ae08745Sheppo vport = &(vldcp->port[portno]); 8031ae08745Sheppo 8041ae08745Sheppo ASSERT(MUTEX_HELD(&vport->minorp->lock)); 8051ae08745Sheppo 8063af08d82Slm66018 D1("i_vldc_close_port: vldc@%d:%d: closing port\n", 8073af08d82Slm66018 vport->inst, vport->minorp->portno); 8083af08d82Slm66018 8094d39be2bSsg70180 vminor = vport->minorp; 8104d39be2bSsg70180 8113af08d82Slm66018 switch (vport->status) { 8123af08d82Slm66018 case VLDC_PORT_CLOSED: 8131ae08745Sheppo /* nothing to do */ 8141ae08745Sheppo DWARN("i_vldc_close_port: port %d in an unexpected " 8151ae08745Sheppo "state (%d)\n", portno, vport->status); 8161ae08745Sheppo return (DDI_SUCCESS); 8173af08d82Slm66018 8183af08d82Slm66018 case VLDC_PORT_READY: 8193af08d82Slm66018 case VLDC_PORT_RESET: 8204d39be2bSsg70180 do { 8213af08d82Slm66018 rv = i_vldc_ldc_close(vport); 8224d39be2bSsg70180 if (rv != EAGAIN) 8234d39be2bSsg70180 break; 8244d39be2bSsg70180 8254d39be2bSsg70180 /* 8264d39be2bSsg70180 * EAGAIN indicates that ldc_close() failed because 8274d39be2bSsg70180 * ldc callback thread is active for the channel. 8284d39be2bSsg70180 * cv_timedwait() is used to release vminor->lock and 8294d39be2bSsg70180 * allow ldc callback thread to complete. 8304d39be2bSsg70180 * after waking up, check if the port has been closed 8314d39be2bSsg70180 * by another thread in the meantime. 8324d39be2bSsg70180 */ 833*d3d50737SRafael Vanoni (void) cv_reltimedwait(&vminor->cv, &vminor->lock, 834*d3d50737SRafael Vanoni drv_usectohz(vldc_close_delay), TR_CLOCK_TICK); 8354d39be2bSsg70180 rv = 0; 8364d39be2bSsg70180 } while (vport->status != VLDC_PORT_CLOSED); 8374d39be2bSsg70180 8384d39be2bSsg70180 if ((rv != 0) || (vport->status == VLDC_PORT_CLOSED)) 839cb112a14Slm66018 return (rv); 8404d39be2bSsg70180 8413af08d82Slm66018 break; 842cb112a14Slm66018 843cb112a14Slm66018 case VLDC_PORT_OPEN: 844cb112a14Slm66018 break; 845cb112a14Slm66018 846cb112a14Slm66018 default: 847cb112a14Slm66018 DWARN("i_vldc_close_port: port %d in an unexpected " 848cb112a14Slm66018 "state (%d)\n", portno, vport->status); 849cb112a14Slm66018 ASSERT(0); /* fail quickly to help diagnosis */ 850cb112a14Slm66018 return (EINVAL); 8511ae08745Sheppo } 8521ae08745Sheppo 8531ae08745Sheppo ASSERT(vport->status == VLDC_PORT_OPEN); 8541ae08745Sheppo 8551ae08745Sheppo /* free memory */ 8561ae08745Sheppo kmem_free(vport->send_buf, vport->mtu); 8571ae08745Sheppo kmem_free(vport->recv_buf, vport->mtu); 8581ae08745Sheppo 8594d39be2bSsg70180 if (strcmp(vminor->sname, VLDC_HVCTL_SVCNAME) == 0) 860d10e4ef2Snarayan kmem_free(vport->cookie_buf, vldc_max_cookie); 861d10e4ef2Snarayan 8621ae08745Sheppo vport->status = VLDC_PORT_CLOSED; 8631ae08745Sheppo 8641ae08745Sheppo return (rv); 8651ae08745Sheppo } 8661ae08745Sheppo 8671ae08745Sheppo /* 8681ae08745Sheppo * attach(9E): attach a device to the system. 8691ae08745Sheppo * called once for each instance of the device on the system. 8701ae08745Sheppo */ 8711ae08745Sheppo static int 8721ae08745Sheppo vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 8731ae08745Sheppo { 8741ae08745Sheppo int i, instance; 8751ae08745Sheppo vldc_t *vldcp; 8761ae08745Sheppo 8771ae08745Sheppo switch (cmd) { 8781ae08745Sheppo 8791ae08745Sheppo case DDI_ATTACH: 8801ae08745Sheppo 8811ae08745Sheppo instance = ddi_get_instance(dip); 8821ae08745Sheppo 8831ae08745Sheppo if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) { 8841ae08745Sheppo return (DDI_FAILURE); 8851ae08745Sheppo } 8861ae08745Sheppo 8871ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 8881ae08745Sheppo if (vldcp == NULL) { 8891ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance); 8901ae08745Sheppo return (ENXIO); 8911ae08745Sheppo } 8921ae08745Sheppo 8931ae08745Sheppo D1("vldc_attach: DDI_ATTACH instance=%d\n", instance); 8941ae08745Sheppo 8951ae08745Sheppo mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL); 8961ae08745Sheppo vldcp->dip = dip; 8971ae08745Sheppo vldcp->detaching = B_FALSE; 8981ae08745Sheppo 8991ae08745Sheppo for (i = 0; i < VLDC_MAX_PORTS; i++) { 9001ae08745Sheppo /* No minor node association to start with */ 9011ae08745Sheppo vldcp->port[i].minorp = NULL; 9021ae08745Sheppo } 9031ae08745Sheppo 9041ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) { 9051ae08745Sheppo mutex_init(&(vldcp->minor_tbl[i].lock), NULL, 9061ae08745Sheppo MUTEX_DRIVER, NULL); 9071ae08745Sheppo cv_init(&(vldcp->minor_tbl[i].cv), NULL, 9081ae08745Sheppo CV_DRIVER, NULL); 9091ae08745Sheppo /* No port association to start with */ 9101ae08745Sheppo vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO; 9111ae08745Sheppo } 9121ae08745Sheppo 9131ae08745Sheppo /* Register for MD update notification */ 9141ae08745Sheppo if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) { 9151ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance); 9161ae08745Sheppo return (DDI_FAILURE); 9171ae08745Sheppo } 9181ae08745Sheppo 9191ae08745Sheppo return (DDI_SUCCESS); 9201ae08745Sheppo 9211ae08745Sheppo case DDI_RESUME: 9221ae08745Sheppo 9231ae08745Sheppo return (DDI_SUCCESS); 9241ae08745Sheppo 9251ae08745Sheppo default: 9261ae08745Sheppo 9271ae08745Sheppo return (DDI_FAILURE); 9281ae08745Sheppo } 9291ae08745Sheppo } 9301ae08745Sheppo 9311ae08745Sheppo /* 9321ae08745Sheppo * detach(9E): detach a device from the system. 9331ae08745Sheppo */ 9341ae08745Sheppo static int 9351ae08745Sheppo vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 9361ae08745Sheppo { 9371ae08745Sheppo int i, instance; 9381ae08745Sheppo vldc_t *vldcp; 9391ae08745Sheppo 9401ae08745Sheppo switch (cmd) { 9411ae08745Sheppo 9421ae08745Sheppo case DDI_DETACH: 9431ae08745Sheppo 9441ae08745Sheppo instance = ddi_get_instance(dip); 9451ae08745Sheppo 9461ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 9471ae08745Sheppo if (vldcp == NULL) { 9481ae08745Sheppo return (DDI_FAILURE); 9491ae08745Sheppo } 9501ae08745Sheppo 9511ae08745Sheppo D1("vldc_detach: DDI_DETACH instance=%d\n", instance); 9521ae08745Sheppo 9531ae08745Sheppo mutex_enter(&vldcp->lock); 9541ae08745Sheppo 9551ae08745Sheppo /* Fail the detach if all ports have not been removed. */ 9561ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) { 9571ae08745Sheppo if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) { 9581ae08745Sheppo D1("vldc_detach: vldc@%d:%d is bound, " 9591ae08745Sheppo "detach failed\n", 9601ae08745Sheppo instance, vldcp->minor_tbl[i].portno); 9611ae08745Sheppo mutex_exit(&vldcp->lock); 9621ae08745Sheppo return (DDI_FAILURE); 9631ae08745Sheppo } 9641ae08745Sheppo } 9651ae08745Sheppo 9661ae08745Sheppo /* 9671ae08745Sheppo * Prevent MDEG from adding new ports before the callback can 9681ae08745Sheppo * be unregistered. The lock can't be held accross the 9691ae08745Sheppo * unregistration call because a callback may be in progress 9701ae08745Sheppo * and blocked on the lock. 9711ae08745Sheppo */ 9721ae08745Sheppo vldcp->detaching = B_TRUE; 9731ae08745Sheppo 9741ae08745Sheppo mutex_exit(&vldcp->lock); 9751ae08745Sheppo 9761ae08745Sheppo if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) { 9771ae08745Sheppo vldcp->detaching = B_FALSE; 9781ae08745Sheppo return (DDI_FAILURE); 9791ae08745Sheppo } 9801ae08745Sheppo 9811ae08745Sheppo /* Tear down all bound ports and free resources. */ 9821ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) { 9831ae08745Sheppo if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) { 9841ae08745Sheppo (void) i_vldc_remove_port(vldcp, i); 9851ae08745Sheppo } 9861ae08745Sheppo mutex_destroy(&(vldcp->minor_tbl[i].lock)); 9871ae08745Sheppo cv_destroy(&(vldcp->minor_tbl[i].cv)); 9881ae08745Sheppo } 9891ae08745Sheppo 9901ae08745Sheppo mutex_destroy(&vldcp->lock); 9911ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance); 9921ae08745Sheppo 9931ae08745Sheppo return (DDI_SUCCESS); 9941ae08745Sheppo 9951ae08745Sheppo case DDI_SUSPEND: 9961ae08745Sheppo 9971ae08745Sheppo return (DDI_SUCCESS); 9981ae08745Sheppo 9991ae08745Sheppo default: 10001ae08745Sheppo 10011ae08745Sheppo return (DDI_FAILURE); 10021ae08745Sheppo } 10031ae08745Sheppo } 10041ae08745Sheppo 10051ae08745Sheppo /* cb_open */ 10061ae08745Sheppo static int 10071ae08745Sheppo vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred) 10081ae08745Sheppo { 10091ae08745Sheppo _NOTE(ARGUNUSED(flag, otyp, cred)) 10101ae08745Sheppo 10111ae08745Sheppo int instance; 10121ae08745Sheppo minor_t minor; 10131ae08745Sheppo uint64_t portno; 10141ae08745Sheppo vldc_t *vldcp; 10151ae08745Sheppo vldc_port_t *vport; 10161ae08745Sheppo vldc_minor_t *vminor; 10171ae08745Sheppo 10181ae08745Sheppo minor = getminor(*devp); 10191ae08745Sheppo instance = VLDCINST(minor); 10201ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 10211ae08745Sheppo if (vldcp == NULL) 10221ae08745Sheppo return (ENXIO); 10231ae08745Sheppo 10241ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 10251ae08745Sheppo mutex_enter(&vminor->lock); 10261ae08745Sheppo portno = vminor->portno; 10271ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 10281ae08745Sheppo mutex_exit(&vminor->lock); 10291ae08745Sheppo return (ENXIO); 10301ae08745Sheppo } 10311ae08745Sheppo 10321ae08745Sheppo vport = &(vldcp->port[portno]); 10331ae08745Sheppo 10341ae08745Sheppo D1("vldc_open: opening vldc@%d:%lu\n", instance, portno); 10351ae08745Sheppo 10361ae08745Sheppo if (vport->status != VLDC_PORT_CLOSED) { 10371ae08745Sheppo mutex_exit(&vminor->lock); 10381ae08745Sheppo return (EBUSY); 10391ae08745Sheppo } 10401ae08745Sheppo 10411ae08745Sheppo vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP); 10421ae08745Sheppo vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP); 10431ae08745Sheppo 1044d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0) 1045d10e4ef2Snarayan vport->cookie_buf = kmem_alloc(vldc_max_cookie, KM_SLEEP); 1046d10e4ef2Snarayan 10471ae08745Sheppo vport->is_stream = B_FALSE; /* assume not a stream */ 10481ae08745Sheppo vport->hanged_up = B_FALSE; 10491ae08745Sheppo 10501ae08745Sheppo vport->status = VLDC_PORT_OPEN; 10511ae08745Sheppo 10521ae08745Sheppo mutex_exit(&vminor->lock); 10531ae08745Sheppo 10541ae08745Sheppo return (DDI_SUCCESS); 10551ae08745Sheppo } 10561ae08745Sheppo 10571ae08745Sheppo /* cb_close */ 10581ae08745Sheppo static int 10591ae08745Sheppo vldc_close(dev_t dev, int flag, int otyp, cred_t *cred) 10601ae08745Sheppo { 10611ae08745Sheppo _NOTE(ARGUNUSED(flag, otyp, cred)) 10621ae08745Sheppo 10631ae08745Sheppo int instance; 10641ae08745Sheppo minor_t minor; 10651ae08745Sheppo uint64_t portno; 10661ae08745Sheppo vldc_t *vldcp; 10671ae08745Sheppo vldc_minor_t *vminor; 10681ae08745Sheppo int rv; 10691ae08745Sheppo 10701ae08745Sheppo minor = getminor(dev); 10711ae08745Sheppo instance = VLDCINST(minor); 10721ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 10731ae08745Sheppo if (vldcp == NULL) { 10741ae08745Sheppo return (ENXIO); 10751ae08745Sheppo } 10761ae08745Sheppo 10771ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 10781ae08745Sheppo mutex_enter(&vminor->lock); 10791ae08745Sheppo portno = vminor->portno; 10801ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 10811ae08745Sheppo mutex_exit(&vminor->lock); 10821ae08745Sheppo return (ENOLINK); 10831ae08745Sheppo } 10841ae08745Sheppo 10851ae08745Sheppo D1("vldc_close: closing vldc@%d:%lu\n", instance, portno); 10861ae08745Sheppo 10871ae08745Sheppo rv = i_vldc_close_port(vldcp, portno); 10881ae08745Sheppo 10891ae08745Sheppo mutex_exit(&vminor->lock); 10901ae08745Sheppo 10911ae08745Sheppo return (rv); 10921ae08745Sheppo } 10931ae08745Sheppo 10941ae08745Sheppo static int 10951ae08745Sheppo vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode) 10961ae08745Sheppo { 10971ae08745Sheppo ldc_attr_t attr; 10981ae08745Sheppo int rv; 10991ae08745Sheppo 11001ae08745Sheppo ASSERT(MUTEX_HELD(&vport->minorp->lock)); 11011ae08745Sheppo 11021ae08745Sheppo /* validate mode */ 11031ae08745Sheppo switch (channel_mode) { 110420ae46ebSha137994 case LDC_MODE_RELIABLE: 11051ae08745Sheppo vport->is_stream = B_TRUE; 11061ae08745Sheppo break; 11071ae08745Sheppo case LDC_MODE_RAW: 11081ae08745Sheppo case LDC_MODE_UNRELIABLE: 11091ae08745Sheppo vport->is_stream = B_FALSE; 11101ae08745Sheppo break; 11111ae08745Sheppo default: 11121ae08745Sheppo return (EINVAL); 11131ae08745Sheppo } 11141ae08745Sheppo 11151ae08745Sheppo if (vport->status == VLDC_PORT_READY) { 11161ae08745Sheppo rv = i_vldc_ldc_close(vport); 11171ae08745Sheppo if (rv != 0) { 11181ae08745Sheppo DWARN("vldc_set_ldc_mode: i_vldc_ldc_close " 11191ae08745Sheppo "failed, rv=%d\n", rv); 11201ae08745Sheppo return (rv); 11211ae08745Sheppo } 11221ae08745Sheppo } 11231ae08745Sheppo 11241ae08745Sheppo D1("vldc_set_ldc_mode: vport status %d, mode %d\n", 11251ae08745Sheppo vport->status, channel_mode); 11261ae08745Sheppo 11271ae08745Sheppo vport->ldc_mode = channel_mode; 11281ae08745Sheppo 11291ae08745Sheppo /* initialize the channel */ 11301ae08745Sheppo attr.devclass = LDC_DEV_SERIAL; 11311ae08745Sheppo attr.instance = ddi_get_instance(vldcp->dip); 1132e1ebb9ecSlm66018 attr.mtu = vport->mtu; 11331ae08745Sheppo attr.mode = vport->ldc_mode; 11341ae08745Sheppo 11351ae08745Sheppo if ((rv = ldc_init(vport->ldc_id, &attr, 11361ae08745Sheppo &vport->ldc_handle)) != 0) { 11371ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv); 11381ae08745Sheppo goto error_init; 11391ae08745Sheppo } 11401ae08745Sheppo 11411ae08745Sheppo /* register it */ 11421ae08745Sheppo if ((rv = ldc_reg_callback(vport->ldc_handle, 11431ae08745Sheppo i_vldc_cb, (caddr_t)vport)) != 0) { 11441ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n", 11451ae08745Sheppo rv); 11461ae08745Sheppo goto error_reg; 11471ae08745Sheppo } 11481ae08745Sheppo 11491ae08745Sheppo /* open the channel */ 11501ae08745Sheppo if ((rv = ldc_open(vport->ldc_handle)) != 0) { 11511ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv); 11521ae08745Sheppo goto error_open; 11531ae08745Sheppo } 11541ae08745Sheppo 11551ae08745Sheppo vport->status = VLDC_PORT_READY; 11561ae08745Sheppo 11571ae08745Sheppo /* 11581ae08745Sheppo * Attempt to bring the channel up, but do not 11591ae08745Sheppo * fail if the other end is not up yet. 11601ae08745Sheppo */ 11611ae08745Sheppo rv = ldc_up(vport->ldc_handle); 11621ae08745Sheppo if (rv == ECONNREFUSED) { 11631ae08745Sheppo D1("vldc_ioctl_opt_op: remote endpoint not up yet\n"); 11641ae08745Sheppo } else if (rv != 0) { 11651ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv); 11661ae08745Sheppo goto error_up; 11671ae08745Sheppo } 11681ae08745Sheppo 1169a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status); 1170a8ea4edeSnarayan if (rv != 0) { 1171a8ea4edeSnarayan DWARN("vldc_ioctl_opt_op: vldc@%d:%d could not get ldc " 1172a8ea4edeSnarayan "status, rv=%d\n", vport->inst, vport->number, rv); 1173a8ea4edeSnarayan goto error_up; 1174a8ea4edeSnarayan } 1175a8ea4edeSnarayan 11761ae08745Sheppo D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n", 11771ae08745Sheppo vport->ldc_id); 11781ae08745Sheppo 11791ae08745Sheppo return (0); 11801ae08745Sheppo 11811ae08745Sheppo error_up: 11821ae08745Sheppo vport->status = VLDC_PORT_OPEN; 11831ae08745Sheppo (void) ldc_close(vport->ldc_handle); 11841ae08745Sheppo error_open: 11851ae08745Sheppo (void) ldc_unreg_callback(vport->ldc_handle); 11861ae08745Sheppo error_reg: 11871ae08745Sheppo (void) ldc_fini(vport->ldc_handle); 11881ae08745Sheppo error_init: 11891ae08745Sheppo return (rv); 11901ae08745Sheppo } 11911ae08745Sheppo 11921ae08745Sheppo /* ioctl to read cookie */ 11931ae08745Sheppo static int 11941ae08745Sheppo i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg, 11951ae08745Sheppo int mode) 11961ae08745Sheppo { 11971ae08745Sheppo vldc_data_t copy_info; 1198d10e4ef2Snarayan uint64_t len, balance, copy_size; 1199d10e4ef2Snarayan caddr_t src_addr, dst_addr; 12001ae08745Sheppo int rv; 12011ae08745Sheppo 12021ae08745Sheppo if (ddi_copyin(arg, ©_info, sizeof (copy_info), mode) == -1) { 12031ae08745Sheppo return (EFAULT); 12041ae08745Sheppo } 12051ae08745Sheppo 1206d10e4ef2Snarayan len = balance = copy_info.length; 1207d10e4ef2Snarayan src_addr = (caddr_t)copy_info.src_addr; 1208d10e4ef2Snarayan dst_addr = (caddr_t)copy_info.dst_addr; 1209d10e4ef2Snarayan while (balance > 0) { 12101ae08745Sheppo 1211d10e4ef2Snarayan /* get the max amount to the copied */ 1212d10e4ef2Snarayan copy_size = MIN(balance, vldc_max_cookie); 12131ae08745Sheppo 12141ae08745Sheppo mutex_enter(&vport->minorp->lock); 12151ae08745Sheppo 1216d10e4ef2Snarayan D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%p " 1217d10e4ef2Snarayan "size 0x%lx to 0x%p\n", vldc_instance, vport->number, 1218d10e4ef2Snarayan dst_addr, copy_size, src_addr); 12191ae08745Sheppo 12201ae08745Sheppo /* read from the HV into the temporary buffer */ 12213af08d82Slm66018 rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf, 1222d10e4ef2Snarayan ©_size, dst_addr, LDC_COPY_IN); 12231ae08745Sheppo if (rv != 0) { 1224d10e4ef2Snarayan DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot " 1225d10e4ef2Snarayan "read address 0x%p, rv=%d\n", 1226d10e4ef2Snarayan vldc_instance, vport->number, dst_addr, rv); 12271ae08745Sheppo mutex_exit(&vport->minorp->lock); 12281ae08745Sheppo return (EFAULT); 12291ae08745Sheppo } 12301ae08745Sheppo 12311ae08745Sheppo D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n", 12321ae08745Sheppo vldc_instance, vport->number); 12331ae08745Sheppo 12341ae08745Sheppo mutex_exit(&vport->minorp->lock); 12351ae08745Sheppo 1236d10e4ef2Snarayan /* 1237d10e4ef2Snarayan * copy data from temporary buffer out to the 1238d10e4ef2Snarayan * caller and free buffer 1239d10e4ef2Snarayan */ 1240d10e4ef2Snarayan rv = ddi_copyout(vport->cookie_buf, src_addr, copy_size, mode); 12411ae08745Sheppo if (rv != 0) { 12421ae08745Sheppo return (EFAULT); 12431ae08745Sheppo } 12441ae08745Sheppo 1245d10e4ef2Snarayan /* adjust len, source and dest */ 1246d10e4ef2Snarayan balance -= copy_size; 1247d10e4ef2Snarayan src_addr += copy_size; 1248d10e4ef2Snarayan dst_addr += copy_size; 1249d10e4ef2Snarayan } 1250d10e4ef2Snarayan 12511ae08745Sheppo /* set the structure to reflect outcome */ 12521ae08745Sheppo copy_info.length = len; 12531ae08745Sheppo if (ddi_copyout(©_info, arg, sizeof (copy_info), mode) != 0) { 12541ae08745Sheppo return (EFAULT); 12551ae08745Sheppo } 12561ae08745Sheppo 12571ae08745Sheppo return (0); 12581ae08745Sheppo } 12591ae08745Sheppo 12601ae08745Sheppo /* ioctl to write cookie */ 12611ae08745Sheppo static int 12621ae08745Sheppo i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg, 12631ae08745Sheppo int mode) 12641ae08745Sheppo { 12651ae08745Sheppo vldc_data_t copy_info; 1266d10e4ef2Snarayan uint64_t len, balance, copy_size; 1267d10e4ef2Snarayan caddr_t src_addr, dst_addr; 12681ae08745Sheppo int rv; 12691ae08745Sheppo 1270d10e4ef2Snarayan if (ddi_copyin(arg, ©_info, sizeof (copy_info), mode) != 0) { 12711ae08745Sheppo return (EFAULT); 12721ae08745Sheppo } 12731ae08745Sheppo 12741ae08745Sheppo D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx " 12751ae08745Sheppo "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr, 12761ae08745Sheppo copy_info.length, copy_info.dst_addr); 12771ae08745Sheppo 1278d10e4ef2Snarayan len = balance = copy_info.length; 1279d10e4ef2Snarayan src_addr = (caddr_t)copy_info.src_addr; 1280d10e4ef2Snarayan dst_addr = (caddr_t)copy_info.dst_addr; 1281d10e4ef2Snarayan while (balance > 0) { 12821ae08745Sheppo 1283d10e4ef2Snarayan /* get the max amount to the copied */ 1284d10e4ef2Snarayan copy_size = MIN(balance, vldc_max_cookie); 1285d10e4ef2Snarayan 1286d10e4ef2Snarayan /* 1287d10e4ef2Snarayan * copy into the temporary buffer the data 1288d10e4ef2Snarayan * to be written to the HV 1289d10e4ef2Snarayan */ 1290d10e4ef2Snarayan if (ddi_copyin((caddr_t)src_addr, vport->cookie_buf, 1291d10e4ef2Snarayan copy_size, mode) != 0) { 12921ae08745Sheppo return (EFAULT); 12931ae08745Sheppo } 12941ae08745Sheppo 12951ae08745Sheppo mutex_enter(&vport->minorp->lock); 12961ae08745Sheppo 12971ae08745Sheppo /* write the data from the temporary buffer to the HV */ 12983af08d82Slm66018 rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf, 1299d10e4ef2Snarayan ©_size, dst_addr, LDC_COPY_OUT); 13001ae08745Sheppo if (rv != 0) { 1301d10e4ef2Snarayan DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d " 1302d10e4ef2Snarayan "failed to write at address 0x%p\n, rv=%d", 1303d10e4ef2Snarayan vldc_instance, vport->number, dst_addr, rv); 13041ae08745Sheppo mutex_exit(&vport->minorp->lock); 13051ae08745Sheppo return (EFAULT); 13061ae08745Sheppo } 13071ae08745Sheppo 13081ae08745Sheppo D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n", 13091ae08745Sheppo vldc_instance, vport->number); 13101ae08745Sheppo 13111ae08745Sheppo mutex_exit(&vport->minorp->lock); 13121ae08745Sheppo 1313d10e4ef2Snarayan /* adjust len, source and dest */ 1314d10e4ef2Snarayan balance -= copy_size; 1315d10e4ef2Snarayan src_addr += copy_size; 1316d10e4ef2Snarayan dst_addr += copy_size; 1317d10e4ef2Snarayan } 13181ae08745Sheppo 13191ae08745Sheppo /* set the structure to reflect outcome */ 13201ae08745Sheppo copy_info.length = len; 13211ae08745Sheppo if (ddi_copyout(©_info, (caddr_t)arg, 13221ae08745Sheppo sizeof (copy_info), mode) != 0) { 13231ae08745Sheppo return (EFAULT); 13241ae08745Sheppo } 13251ae08745Sheppo 13261ae08745Sheppo return (0); 13271ae08745Sheppo } 13281ae08745Sheppo 13291ae08745Sheppo /* vldc specific ioctl option commands */ 13301ae08745Sheppo static int 13311ae08745Sheppo i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode) 13321ae08745Sheppo { 13331ae08745Sheppo vldc_opt_op_t vldc_cmd; 13341ae08745Sheppo uint32_t new_mtu; 13351ae08745Sheppo int rv = 0; 13361ae08745Sheppo 13371ae08745Sheppo if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) { 13381ae08745Sheppo return (EFAULT); 13391ae08745Sheppo } 13401ae08745Sheppo 13411ae08745Sheppo D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel); 13421ae08745Sheppo 13431ae08745Sheppo switch (vldc_cmd.opt_sel) { 13441ae08745Sheppo 13451ae08745Sheppo case VLDC_OPT_MTU_SZ: 13461ae08745Sheppo 13471ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) { 13481ae08745Sheppo vldc_cmd.opt_val = vport->mtu; 13491ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg, 13501ae08745Sheppo sizeof (vldc_cmd), mode) == -1) { 13511ae08745Sheppo return (EFAULT); 13521ae08745Sheppo } 13531ae08745Sheppo } else { 13541ae08745Sheppo new_mtu = vldc_cmd.opt_val; 13551ae08745Sheppo 13561ae08745Sheppo if ((new_mtu < LDC_PACKET_SIZE) || 13571ae08745Sheppo (new_mtu > vldc_max_mtu)) { 13581ae08745Sheppo return (EINVAL); 13591ae08745Sheppo } 13601ae08745Sheppo 13611ae08745Sheppo mutex_enter(&vport->minorp->lock); 13621ae08745Sheppo 13631ae08745Sheppo if ((vport->status != VLDC_PORT_CLOSED) && 13641ae08745Sheppo (new_mtu != vport->mtu)) { 13651ae08745Sheppo /* 13661ae08745Sheppo * The port has buffers allocated since it is 13671ae08745Sheppo * not closed plus the MTU size has changed. 13681ae08745Sheppo * Reallocate the buffers to the new MTU size. 13691ae08745Sheppo */ 13701ae08745Sheppo kmem_free(vport->recv_buf, vport->mtu); 13711ae08745Sheppo vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP); 13721ae08745Sheppo 13731ae08745Sheppo kmem_free(vport->send_buf, vport->mtu); 13741ae08745Sheppo vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP); 13751ae08745Sheppo 13761ae08745Sheppo vport->mtu = new_mtu; 13771ae08745Sheppo } 13781ae08745Sheppo 13791ae08745Sheppo mutex_exit(&vport->minorp->lock); 13801ae08745Sheppo } 13811ae08745Sheppo 13821ae08745Sheppo break; 13831ae08745Sheppo 13841ae08745Sheppo case VLDC_OPT_STATUS: 13851ae08745Sheppo 13861ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) { 13871ae08745Sheppo vldc_cmd.opt_val = vport->status; 13881ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg, 13891ae08745Sheppo sizeof (vldc_cmd), mode) == -1) { 13901ae08745Sheppo return (EFAULT); 13911ae08745Sheppo } 13921ae08745Sheppo } else { 13931ae08745Sheppo return (ENOTSUP); 13941ae08745Sheppo } 13951ae08745Sheppo 13961ae08745Sheppo break; 13971ae08745Sheppo 13981ae08745Sheppo case VLDC_OPT_MODE: 13991ae08745Sheppo 14001ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) { 14011ae08745Sheppo vldc_cmd.opt_val = vport->ldc_mode; 14021ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg, 14031ae08745Sheppo sizeof (vldc_cmd), mode) == -1) { 14041ae08745Sheppo return (EFAULT); 14051ae08745Sheppo } 14061ae08745Sheppo } else { 14071ae08745Sheppo mutex_enter(&vport->minorp->lock); 14081ae08745Sheppo rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val); 14091ae08745Sheppo mutex_exit(&vport->minorp->lock); 14101ae08745Sheppo } 14111ae08745Sheppo 14121ae08745Sheppo break; 14131ae08745Sheppo 14141ae08745Sheppo default: 14151ae08745Sheppo 14161ae08745Sheppo D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel); 14171ae08745Sheppo return (ENOTSUP); 14181ae08745Sheppo } 14191ae08745Sheppo 14201ae08745Sheppo return (rv); 14211ae08745Sheppo } 14221ae08745Sheppo 14231ae08745Sheppo /* cb_ioctl */ 14241ae08745Sheppo static int 14251ae08745Sheppo vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 14261ae08745Sheppo int *rvalp) 14271ae08745Sheppo { 14281ae08745Sheppo _NOTE(ARGUNUSED(credp, rvalp)) 14291ae08745Sheppo 14301ae08745Sheppo int rv = EINVAL; 14311ae08745Sheppo int instance; 14321ae08745Sheppo minor_t minor; 14331ae08745Sheppo uint64_t portno; 14341ae08745Sheppo vldc_t *vldcp; 14351ae08745Sheppo vldc_port_t *vport; 14361ae08745Sheppo vldc_minor_t *vminor; 14371ae08745Sheppo 14381ae08745Sheppo minor = getminor(dev); 14391ae08745Sheppo instance = VLDCINST(minor); 14401ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 14411ae08745Sheppo if (vldcp == NULL) { 14421ae08745Sheppo return (ENXIO); 14431ae08745Sheppo } 14441ae08745Sheppo 14451ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 14461ae08745Sheppo mutex_enter(&vminor->lock); 14471ae08745Sheppo portno = vminor->portno; 14481ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 14491ae08745Sheppo mutex_exit(&vminor->lock); 14501ae08745Sheppo return (ENOLINK); 14511ae08745Sheppo } 14521ae08745Sheppo vminor->in_use += 1; 14531ae08745Sheppo mutex_exit(&vminor->lock); 14541ae08745Sheppo 14551ae08745Sheppo vport = &(vldcp->port[portno]); 14561ae08745Sheppo 14571ae08745Sheppo D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd); 14581ae08745Sheppo 14591ae08745Sheppo switch (cmd) { 14601ae08745Sheppo 14611ae08745Sheppo case VLDC_IOCTL_OPT_OP: 14621ae08745Sheppo rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg, mode); 14631ae08745Sheppo break; 14641ae08745Sheppo 14651ae08745Sheppo case VLDC_IOCTL_READ_COOKIE: 1466d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) { 1467d10e4ef2Snarayan rv = EINVAL; 1468d10e4ef2Snarayan break; 1469d10e4ef2Snarayan } 14701ae08745Sheppo rv = i_vldc_ioctl_read_cookie(vport, instance, 14711ae08745Sheppo (void *)arg, mode); 14721ae08745Sheppo break; 14731ae08745Sheppo 14741ae08745Sheppo case VLDC_IOCTL_WRITE_COOKIE: 1475d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) { 1476d10e4ef2Snarayan rv = EINVAL; 1477d10e4ef2Snarayan break; 1478d10e4ef2Snarayan } 14791ae08745Sheppo rv = i_vldc_ioctl_write_cookie(vport, instance, 14801ae08745Sheppo (void *)arg, mode); 14811ae08745Sheppo break; 14821ae08745Sheppo 14831ae08745Sheppo default: 14841ae08745Sheppo DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n", 14851ae08745Sheppo instance, portno, cmd); 14861ae08745Sheppo rv = EINVAL; 14871ae08745Sheppo break; 14881ae08745Sheppo } 14891ae08745Sheppo 14901ae08745Sheppo mutex_enter(&vminor->lock); 14911ae08745Sheppo vminor->in_use -= 1; 14921ae08745Sheppo if (vminor->in_use == 0) { 14931ae08745Sheppo cv_signal(&vminor->cv); 14941ae08745Sheppo } 14951ae08745Sheppo mutex_exit(&vminor->lock); 14961ae08745Sheppo 14971ae08745Sheppo D1("vldc_ioctl: rv=%d\n", rv); 14981ae08745Sheppo 14991ae08745Sheppo return (rv); 15001ae08745Sheppo } 15011ae08745Sheppo 15021ae08745Sheppo /* cb_read */ 15031ae08745Sheppo static int 15041ae08745Sheppo vldc_read(dev_t dev, struct uio *uiop, cred_t *credp) 15051ae08745Sheppo { 15061ae08745Sheppo _NOTE(ARGUNUSED(credp)) 15071ae08745Sheppo 15081ae08745Sheppo int instance; 15091ae08745Sheppo minor_t minor; 15101ae08745Sheppo size_t size = 0; 15111ae08745Sheppo uint64_t portno; 15121ae08745Sheppo vldc_t *vldcp; 15131ae08745Sheppo vldc_port_t *vport; 15141ae08745Sheppo vldc_minor_t *vminor; 15151ae08745Sheppo int rv = 0; 15161ae08745Sheppo 15171ae08745Sheppo minor = getminor(dev); 15181ae08745Sheppo instance = VLDCINST(minor); 15191ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 15201ae08745Sheppo if (vldcp == NULL) { 15211ae08745Sheppo return (ENXIO); 15221ae08745Sheppo } 15231ae08745Sheppo 15241ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 15251ae08745Sheppo mutex_enter(&vminor->lock); 15261ae08745Sheppo portno = vminor->portno; 15271ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 15281ae08745Sheppo mutex_exit(&vminor->lock); 15291ae08745Sheppo return (ENOLINK); 15301ae08745Sheppo } 15311ae08745Sheppo 15321ae08745Sheppo D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno); 15331ae08745Sheppo 15341ae08745Sheppo vport = &(vldcp->port[portno]); 15351ae08745Sheppo 15361ae08745Sheppo /* check the port status */ 15371ae08745Sheppo if (vport->status != VLDC_PORT_READY) { 15381ae08745Sheppo DWARN("vldc_read: vldc@%d:%lu not in the ready state\n", 15391ae08745Sheppo instance, portno); 15401ae08745Sheppo mutex_exit(&vminor->lock); 15411ae08745Sheppo return (ENOTACTIVE); 15421ae08745Sheppo } 15431ae08745Sheppo 15441ae08745Sheppo /* read data */ 15451ae08745Sheppo size = MIN(vport->mtu, uiop->uio_resid); 15461ae08745Sheppo rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size); 15471ae08745Sheppo 15481ae08745Sheppo D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n", 15491ae08745Sheppo instance, portno, size, rv); 15501ae08745Sheppo 15511ae08745Sheppo if (rv == 0) { 15521ae08745Sheppo if (size != 0) { 15531ae08745Sheppo rv = uiomove(vport->recv_buf, size, UIO_READ, uiop); 15541ae08745Sheppo } else { 15551ae08745Sheppo rv = EWOULDBLOCK; 15561ae08745Sheppo } 15571ae08745Sheppo } else { 15581ae08745Sheppo switch (rv) { 15591ae08745Sheppo case ENOBUFS: 15601ae08745Sheppo break; 15611ae08745Sheppo case ETIMEDOUT: 15621ae08745Sheppo case EWOULDBLOCK: 15631ae08745Sheppo rv = EWOULDBLOCK; 15641ae08745Sheppo break; 15651ae08745Sheppo default: 15661ae08745Sheppo rv = ECONNRESET; 15671ae08745Sheppo break; 15681ae08745Sheppo } 15691ae08745Sheppo } 15701ae08745Sheppo 15711ae08745Sheppo mutex_exit(&vminor->lock); 15721ae08745Sheppo 15731ae08745Sheppo return (rv); 15741ae08745Sheppo } 15751ae08745Sheppo 15761ae08745Sheppo /* cb_write */ 15771ae08745Sheppo static int 15781ae08745Sheppo vldc_write(dev_t dev, struct uio *uiop, cred_t *credp) 15791ae08745Sheppo { 15801ae08745Sheppo _NOTE(ARGUNUSED(credp)) 15811ae08745Sheppo 15821ae08745Sheppo int instance; 15831ae08745Sheppo minor_t minor; 15841ae08745Sheppo size_t size; 15851ae08745Sheppo size_t orig_size; 15861ae08745Sheppo uint64_t portno; 15871ae08745Sheppo vldc_t *vldcp; 15881ae08745Sheppo vldc_port_t *vport; 15891ae08745Sheppo vldc_minor_t *vminor; 15901ae08745Sheppo int rv = EINVAL; 15911ae08745Sheppo 15921ae08745Sheppo minor = getminor(dev); 15931ae08745Sheppo instance = VLDCINST(minor); 15941ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 15951ae08745Sheppo if (vldcp == NULL) { 15961ae08745Sheppo return (ENXIO); 15971ae08745Sheppo } 15981ae08745Sheppo 15991ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 16001ae08745Sheppo mutex_enter(&vminor->lock); 16011ae08745Sheppo portno = vminor->portno; 16021ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 16031ae08745Sheppo mutex_exit(&vminor->lock); 16041ae08745Sheppo return (ENOLINK); 16051ae08745Sheppo } 16061ae08745Sheppo 16071ae08745Sheppo vport = &(vldcp->port[portno]); 16081ae08745Sheppo 16091ae08745Sheppo /* check the port status */ 16101ae08745Sheppo if (vport->status != VLDC_PORT_READY) { 16111ae08745Sheppo DWARN("vldc_write: vldc@%d:%lu not in the ready state\n", 16121ae08745Sheppo instance, portno); 16131ae08745Sheppo mutex_exit(&vminor->lock); 16141ae08745Sheppo return (ENOTACTIVE); 16151ae08745Sheppo } 16161ae08745Sheppo 16171ae08745Sheppo orig_size = uiop->uio_resid; 16181ae08745Sheppo size = orig_size; 16191ae08745Sheppo 16201ae08745Sheppo if (size > vport->mtu) { 16211ae08745Sheppo if (vport->is_stream) { 16221ae08745Sheppo /* can only send MTU size at a time */ 16231ae08745Sheppo size = vport->mtu; 16241ae08745Sheppo } else { 16251ae08745Sheppo mutex_exit(&vminor->lock); 16261ae08745Sheppo return (EMSGSIZE); 16271ae08745Sheppo } 16281ae08745Sheppo } 16291ae08745Sheppo 16301ae08745Sheppo D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno, 16311ae08745Sheppo size); 16321ae08745Sheppo 16331ae08745Sheppo rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop); 16341ae08745Sheppo if (rv == 0) { 16351ae08745Sheppo rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf, 16361ae08745Sheppo &size); 16371ae08745Sheppo if (rv != 0) { 16381ae08745Sheppo DWARN("vldc_write: vldc@%d:%lu failed writing %lu " 16391ae08745Sheppo "bytes rv=%d\n", instance, portno, size, rv); 16401ae08745Sheppo } 16411ae08745Sheppo } else { 16421ae08745Sheppo size = 0; 16431ae08745Sheppo } 16441ae08745Sheppo 16451ae08745Sheppo mutex_exit(&vminor->lock); 16461ae08745Sheppo 16471ae08745Sheppo /* resid is total number of bytes *not* sent */ 16481ae08745Sheppo uiop->uio_resid = orig_size - size; 16491ae08745Sheppo 16501ae08745Sheppo return (rv); 16511ae08745Sheppo } 16521ae08745Sheppo 16531ae08745Sheppo /* cb_chpoll */ 16541ae08745Sheppo static int 16551ae08745Sheppo vldc_chpoll(dev_t dev, short events, int anyyet, short *reventsp, 16561ae08745Sheppo struct pollhead **phpp) 16571ae08745Sheppo { 16581ae08745Sheppo int instance; 16591ae08745Sheppo minor_t minor; 16601ae08745Sheppo uint64_t portno; 16611ae08745Sheppo vldc_t *vldcp; 16621ae08745Sheppo vldc_port_t *vport; 16631ae08745Sheppo vldc_minor_t *vminor; 1664e1ebb9ecSlm66018 boolean_t haspkts; 16651ae08745Sheppo 16661ae08745Sheppo minor = getminor(dev); 16671ae08745Sheppo instance = VLDCINST(minor); 16681ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance); 16691ae08745Sheppo if (vldcp == NULL) { 16701ae08745Sheppo return (ENXIO); 16711ae08745Sheppo } 16721ae08745Sheppo 16731ae08745Sheppo vminor = VLDCMINOR(vldcp, minor); 16741ae08745Sheppo mutex_enter(&vminor->lock); 16751ae08745Sheppo portno = vminor->portno; 16761ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) { 16771ae08745Sheppo mutex_exit(&vminor->lock); 16781ae08745Sheppo return (ENOLINK); 16791ae08745Sheppo } 16801ae08745Sheppo 16811ae08745Sheppo vport = &(vldcp->port[portno]); 16821ae08745Sheppo 16831ae08745Sheppo /* check the port status */ 16841ae08745Sheppo if (vport->status != VLDC_PORT_READY) { 16851ae08745Sheppo mutex_exit(&vminor->lock); 16861ae08745Sheppo return (ENOTACTIVE); 16871ae08745Sheppo } 16881ae08745Sheppo 16891ae08745Sheppo D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n", 16901ae08745Sheppo instance, portno, events); 16911ae08745Sheppo 16921ae08745Sheppo *reventsp = 0; 16931ae08745Sheppo 1694a8ea4edeSnarayan if (vport->ldc_status == LDC_UP) { 16951ae08745Sheppo /* 16961ae08745Sheppo * Check if the receive queue is empty and if not, signal that 16971ae08745Sheppo * there is data ready to read. 16981ae08745Sheppo */ 16991ae08745Sheppo if (events & POLLIN) { 1700e1ebb9ecSlm66018 if ((ldc_chkq(vport->ldc_handle, &haspkts) == 0) && 1701e1ebb9ecSlm66018 haspkts) { 17021ae08745Sheppo *reventsp |= POLLIN; 17031ae08745Sheppo } 17041ae08745Sheppo } 17051ae08745Sheppo 17061ae08745Sheppo if (events & POLLOUT) 17071ae08745Sheppo *reventsp |= POLLOUT; 17081ae08745Sheppo 17091ae08745Sheppo } else if (vport->hanged_up) { 17101ae08745Sheppo *reventsp |= POLLHUP; 17111ae08745Sheppo vport->hanged_up = B_FALSE; 17121ae08745Sheppo } 17131ae08745Sheppo 17141ae08745Sheppo mutex_exit(&vminor->lock); 17151ae08745Sheppo 17161ae08745Sheppo if (((*reventsp) == 0) && (!anyyet)) { 17171ae08745Sheppo *phpp = &vport->poll; 17181ae08745Sheppo } 17191ae08745Sheppo 17201ae08745Sheppo D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n", 17211ae08745Sheppo instance, portno, events, *reventsp); 17221ae08745Sheppo 17231ae08745Sheppo return (0); 17241ae08745Sheppo } 1725