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 /* 231ae08745Sheppo * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 241ae08745Sheppo * Use is subject to license terms. 251ae08745Sheppo */ 261ae08745Sheppo 271ae08745Sheppo #pragma ident "%Z%%M% %I% %E% SMI" 281ae08745Sheppo 291ae08745Sheppo #include <sys/types.h> 301ae08745Sheppo #include <sys/errno.h> 311ae08745Sheppo #include <sys/debug.h> 321ae08745Sheppo #include <sys/time.h> 331ae08745Sheppo #include <sys/sysmacros.h> 341ae08745Sheppo #include <sys/systm.h> 351ae08745Sheppo #include <sys/user.h> 361ae08745Sheppo #include <sys/stropts.h> 371ae08745Sheppo #include <sys/stream.h> 381ae08745Sheppo #include <sys/strlog.h> 391ae08745Sheppo #include <sys/strsubr.h> 401ae08745Sheppo #include <sys/cmn_err.h> 411ae08745Sheppo #include <sys/cpu.h> 421ae08745Sheppo #include <sys/kmem.h> 431ae08745Sheppo #include <sys/conf.h> 441ae08745Sheppo #include <sys/ddi.h> 451ae08745Sheppo #include <sys/sunddi.h> 461ae08745Sheppo #include <sys/ksynch.h> 471ae08745Sheppo #include <sys/stat.h> 481ae08745Sheppo #include <sys/kstat.h> 491ae08745Sheppo #include <sys/vtrace.h> 501ae08745Sheppo #include <sys/strsun.h> 511ae08745Sheppo #include <sys/dlpi.h> 521ae08745Sheppo #include <sys/ethernet.h> 531ae08745Sheppo #include <net/if.h> 541ae08745Sheppo #include <sys/varargs.h> 551ae08745Sheppo #include <sys/machsystm.h> 561ae08745Sheppo #include <sys/modctl.h> 571ae08745Sheppo #include <sys/modhash.h> 581ae08745Sheppo #include <sys/mac.h> 59*ba2e4443Sseb #include <sys/mac_ether.h> 601ae08745Sheppo #include <sys/taskq.h> 611ae08745Sheppo #include <sys/note.h> 621ae08745Sheppo #include <sys/mach_descrip.h> 631ae08745Sheppo #include <sys/mac.h> 641ae08745Sheppo #include <sys/mdeg.h> 651ae08745Sheppo #include <sys/ldc.h> 661ae08745Sheppo #include <sys/vsw_fdb.h> 671ae08745Sheppo #include <sys/vsw.h> 681ae08745Sheppo #include <sys/vio_mailbox.h> 691ae08745Sheppo #include <sys/vnet_mailbox.h> 701ae08745Sheppo #include <sys/vnet_common.h> 711ae08745Sheppo 721ae08745Sheppo /* 731ae08745Sheppo * Function prototypes. 741ae08745Sheppo */ 751ae08745Sheppo static int vsw_attach(dev_info_t *, ddi_attach_cmd_t); 761ae08745Sheppo static int vsw_detach(dev_info_t *, ddi_detach_cmd_t); 771ae08745Sheppo static int vsw_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 781ae08745Sheppo static void vsw_get_md_properties(vsw_t *vswp); 791ae08745Sheppo static int vsw_setup_layer2(vsw_t *); 801ae08745Sheppo static int vsw_setup_layer3(vsw_t *); 811ae08745Sheppo 821ae08745Sheppo /* MAC layer routines */ 831ae08745Sheppo static int vsw_mac_attach(vsw_t *vswp); 841ae08745Sheppo static void vsw_mac_detach(vsw_t *vswp); 851ae08745Sheppo static void vsw_notify_cb(void *, mac_notify_type_t); 861ae08745Sheppo static void vsw_rx_cb(void *, mac_resource_handle_t, mblk_t *); 871ae08745Sheppo static mblk_t *vsw_tx_msg(vsw_t *, mblk_t *); 881ae08745Sheppo static int vsw_mac_register(vsw_t *); 891ae08745Sheppo static int vsw_mac_unregister(vsw_t *); 90*ba2e4443Sseb static int vsw_m_stat(void *, uint_t, uint64_t *); 911ae08745Sheppo static void vsw_m_stop(void *arg); 921ae08745Sheppo static int vsw_m_start(void *arg); 931ae08745Sheppo static int vsw_m_unicst(void *arg, const uint8_t *); 941ae08745Sheppo static int vsw_m_multicst(void *arg, boolean_t, const uint8_t *); 951ae08745Sheppo static int vsw_m_promisc(void *arg, boolean_t); 961ae08745Sheppo static mblk_t *vsw_m_tx(void *arg, mblk_t *); 971ae08745Sheppo 981ae08745Sheppo /* MDEG routines */ 991ae08745Sheppo static void vsw_mdeg_register(vsw_t *vswp); 1001ae08745Sheppo static void vsw_mdeg_unregister(vsw_t *vswp); 1011ae08745Sheppo static int vsw_mdeg_cb(void *cb_argp, mdeg_result_t *); 1021ae08745Sheppo 1031ae08745Sheppo /* Port add/deletion routines */ 1041ae08745Sheppo static int vsw_port_add(vsw_t *vswp, md_t *mdp, mde_cookie_t *node); 1051ae08745Sheppo static int vsw_port_attach(vsw_t *vswp, int p_instance, 1061ae08745Sheppo uint64_t *ldcids, int nids, struct ether_addr *macaddr); 1071ae08745Sheppo static int vsw_detach_ports(vsw_t *vswp); 1081ae08745Sheppo static int vsw_port_detach(vsw_t *vswp, int p_instance); 1091ae08745Sheppo static int vsw_port_delete(vsw_port_t *port); 1101ae08745Sheppo static int vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id); 1111ae08745Sheppo static int vsw_ldc_detach(vsw_port_t *port, uint64_t ldc_id); 1121ae08745Sheppo static int vsw_init_ldcs(vsw_port_t *port); 1131ae08745Sheppo static int vsw_uninit_ldcs(vsw_port_t *port); 1141ae08745Sheppo static int vsw_ldc_init(vsw_ldc_t *ldcp); 1151ae08745Sheppo static int vsw_ldc_uninit(vsw_ldc_t *ldcp); 1161ae08745Sheppo static int vsw_drain_ldcs(vsw_port_t *port); 1171ae08745Sheppo static int vsw_drain_port_taskq(vsw_port_t *port); 1181ae08745Sheppo static void vsw_marker_task(void *); 1191ae08745Sheppo static vsw_port_t *vsw_lookup_port(vsw_t *vswp, int p_instance); 1201ae08745Sheppo static int vsw_plist_del_node(vsw_t *, vsw_port_t *port); 1211ae08745Sheppo 1221ae08745Sheppo /* Interrupt routines */ 1231ae08745Sheppo static uint_t vsw_ldc_cb(uint64_t cb, caddr_t arg); 1241ae08745Sheppo 1251ae08745Sheppo /* Handshake routines */ 1261ae08745Sheppo static void vsw_restart_handshake(vsw_ldc_t *); 1271ae08745Sheppo static int vsw_check_flag(vsw_ldc_t *, int, uint64_t); 1281ae08745Sheppo static void vsw_next_milestone(vsw_ldc_t *); 1291ae08745Sheppo static int vsw_supported_version(vio_ver_msg_t *); 1301ae08745Sheppo 1311ae08745Sheppo /* Data processing routines */ 1321ae08745Sheppo static void vsw_process_pkt(void *); 1331ae08745Sheppo static void vsw_dispatch_ctrl_task(vsw_ldc_t *, void *, vio_msg_tag_t); 1341ae08745Sheppo static void vsw_process_ctrl_pkt(void *); 1351ae08745Sheppo static void vsw_process_ctrl_ver_pkt(vsw_ldc_t *, void *); 1361ae08745Sheppo static void vsw_process_ctrl_attr_pkt(vsw_ldc_t *, void *); 1371ae08745Sheppo static void vsw_process_ctrl_mcst_pkt(vsw_ldc_t *, void *); 1381ae08745Sheppo static void vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *, void *); 1391ae08745Sheppo static void vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *, void *); 1401ae08745Sheppo static void vsw_process_ctrl_rdx_pkt(vsw_ldc_t *, void *); 1411ae08745Sheppo static void vsw_process_data_pkt(vsw_ldc_t *, void *, vio_msg_tag_t); 1421ae08745Sheppo static void vsw_process_data_dring_pkt(vsw_ldc_t *, void *); 1431ae08745Sheppo static void vsw_process_data_raw_pkt(vsw_ldc_t *, void *); 1441ae08745Sheppo static void vsw_process_data_ibnd_pkt(vsw_ldc_t *, void *); 1451ae08745Sheppo static void vsw_process_err_pkt(vsw_ldc_t *, void *, vio_msg_tag_t); 1461ae08745Sheppo 1471ae08745Sheppo /* Switching/data transmit routines */ 1481ae08745Sheppo static void vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller, 1491ae08745Sheppo vsw_port_t *port, mac_resource_handle_t); 1501ae08745Sheppo static void vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller, 1511ae08745Sheppo vsw_port_t *port, mac_resource_handle_t); 1521ae08745Sheppo static int vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, 1531ae08745Sheppo vsw_port_t *port); 1541ae08745Sheppo static int vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, 1551ae08745Sheppo vsw_port_t *port); 1561ae08745Sheppo static int vsw_portsend(vsw_port_t *, mblk_t *); 1571ae08745Sheppo static int vsw_dringsend(vsw_ldc_t *, mblk_t *); 1581ae08745Sheppo static int vsw_descrsend(vsw_ldc_t *, mblk_t *); 1591ae08745Sheppo 1601ae08745Sheppo /* Packet creation routines */ 1611ae08745Sheppo static void vsw_send_ver(vsw_ldc_t *); 1621ae08745Sheppo static void vsw_send_attr(vsw_ldc_t *); 1631ae08745Sheppo static vio_dring_reg_msg_t *vsw_create_dring_info_pkt(vsw_ldc_t *); 1641ae08745Sheppo static void vsw_send_dring_info(vsw_ldc_t *); 1651ae08745Sheppo static void vsw_send_rdx(vsw_ldc_t *); 1661ae08745Sheppo 1671ae08745Sheppo static void vsw_send_msg(vsw_ldc_t *, void *, int); 1681ae08745Sheppo 1691ae08745Sheppo /* Forwarding database (FDB) routines */ 1701ae08745Sheppo static int vsw_add_fdb(vsw_t *vswp, vsw_port_t *port); 1711ae08745Sheppo static int vsw_del_fdb(vsw_t *vswp, vsw_port_t *port); 1721ae08745Sheppo static vsw_port_t *vsw_lookup_fdb(vsw_t *vswp, struct ether_header *); 1731ae08745Sheppo static int vsw_add_rem_mcst(vnet_mcast_msg_t *, vsw_port_t *); 1741ae08745Sheppo static int vsw_add_mcst(vsw_t *, uint8_t, uint64_t, void *); 1751ae08745Sheppo static int vsw_del_mcst(vsw_t *, uint8_t, uint64_t, void *); 1761ae08745Sheppo static void vsw_del_addr(uint8_t, void *, uint64_t); 1771ae08745Sheppo static void vsw_del_mcst_port(vsw_port_t *); 1781ae08745Sheppo static void vsw_del_mcst_vsw(vsw_t *); 1791ae08745Sheppo 1801ae08745Sheppo /* Dring routines */ 1811ae08745Sheppo static dring_info_t *vsw_create_dring(vsw_ldc_t *); 1821ae08745Sheppo static void vsw_create_privring(vsw_ldc_t *); 1831ae08745Sheppo static int vsw_setup_ring(vsw_ldc_t *ldcp, dring_info_t *dp); 1841ae08745Sheppo static int vsw_dring_find_free_desc(dring_info_t *, vsw_private_desc_t **, 1851ae08745Sheppo int *); 1861ae08745Sheppo static void vsw_dring_priv2pub(vsw_private_desc_t *); 1871ae08745Sheppo static dring_info_t *vsw_ident2dring(lane_t *, uint64_t); 1881ae08745Sheppo 1891ae08745Sheppo static void vsw_set_lane_attr(vsw_t *, lane_t *); 1901ae08745Sheppo static int vsw_check_attr(vnet_attr_msg_t *, vsw_port_t *); 1911ae08745Sheppo static int vsw_dring_match(dring_info_t *dp, vio_dring_reg_msg_t *msg); 1921ae08745Sheppo static int vsw_mem_cookie_match(ldc_mem_cookie_t *, ldc_mem_cookie_t *); 1931ae08745Sheppo static int vsw_check_dring_info(vio_dring_reg_msg_t *); 1941ae08745Sheppo 1951ae08745Sheppo /* Misc support routines */ 1961ae08745Sheppo static caddr_t vsw_print_ethaddr(uint8_t *addr, char *ebuf); 1971ae08745Sheppo 1981ae08745Sheppo static void vsw_free_lane_resources(vsw_ldc_t *, uint64_t); 1991ae08745Sheppo static int vsw_free_ring(dring_info_t *); 2001ae08745Sheppo 2011ae08745Sheppo /* Debugging routines */ 2021ae08745Sheppo static void dump_flags(uint64_t); 2031ae08745Sheppo static void display_state(void); 2041ae08745Sheppo static void display_lane(lane_t *); 2051ae08745Sheppo static void display_ring(dring_info_t *); 2061ae08745Sheppo 2071ae08745Sheppo int vsw_num_handshakes = 3; /* # of handshake attempts */ 2081ae08745Sheppo int vsw_wretries = 100; /* # of write attempts */ 2091ae08745Sheppo 2101ae08745Sheppo /* 2111ae08745Sheppo * mode specific frame switching function 2121ae08745Sheppo */ 2131ae08745Sheppo void (*vsw_switch_frame)(vsw_t *, mblk_t *, int, vsw_port_t *, 2141ae08745Sheppo mac_resource_handle_t); 2151ae08745Sheppo 216*ba2e4443Sseb static mac_callbacks_t vsw_m_callbacks = { 217*ba2e4443Sseb 0, 218*ba2e4443Sseb vsw_m_stat, 219*ba2e4443Sseb vsw_m_start, 220*ba2e4443Sseb vsw_m_stop, 221*ba2e4443Sseb vsw_m_promisc, 222*ba2e4443Sseb vsw_m_multicst, 223*ba2e4443Sseb vsw_m_unicst, 224*ba2e4443Sseb vsw_m_tx, 225*ba2e4443Sseb NULL, 226*ba2e4443Sseb NULL, 227*ba2e4443Sseb NULL 228*ba2e4443Sseb }; 229*ba2e4443Sseb 2301ae08745Sheppo static struct cb_ops vsw_cb_ops = { 2311ae08745Sheppo nulldev, /* cb_open */ 2321ae08745Sheppo nulldev, /* cb_close */ 2331ae08745Sheppo nodev, /* cb_strategy */ 2341ae08745Sheppo nodev, /* cb_print */ 2351ae08745Sheppo nodev, /* cb_dump */ 2361ae08745Sheppo nodev, /* cb_read */ 2371ae08745Sheppo nodev, /* cb_write */ 2381ae08745Sheppo nodev, /* cb_ioctl */ 2391ae08745Sheppo nodev, /* cb_devmap */ 2401ae08745Sheppo nodev, /* cb_mmap */ 2411ae08745Sheppo nodev, /* cb_segmap */ 2421ae08745Sheppo nochpoll, /* cb_chpoll */ 2431ae08745Sheppo ddi_prop_op, /* cb_prop_op */ 2441ae08745Sheppo NULL, /* cb_stream */ 2451ae08745Sheppo D_MP, /* cb_flag */ 2461ae08745Sheppo CB_REV, /* rev */ 2471ae08745Sheppo nodev, /* int (*cb_aread)() */ 2481ae08745Sheppo nodev /* int (*cb_awrite)() */ 2491ae08745Sheppo }; 2501ae08745Sheppo 2511ae08745Sheppo static struct dev_ops vsw_ops = { 2521ae08745Sheppo DEVO_REV, /* devo_rev */ 2531ae08745Sheppo 0, /* devo_refcnt */ 2541ae08745Sheppo vsw_getinfo, /* devo_getinfo */ 2551ae08745Sheppo nulldev, /* devo_identify */ 2561ae08745Sheppo nulldev, /* devo_probe */ 2571ae08745Sheppo vsw_attach, /* devo_attach */ 2581ae08745Sheppo vsw_detach, /* devo_detach */ 2591ae08745Sheppo nodev, /* devo_reset */ 2601ae08745Sheppo &vsw_cb_ops, /* devo_cb_ops */ 2611ae08745Sheppo (struct bus_ops *)NULL, /* devo_bus_ops */ 2621ae08745Sheppo ddi_power /* devo_power */ 2631ae08745Sheppo }; 2641ae08745Sheppo 2651ae08745Sheppo extern struct mod_ops mod_driverops; 2661ae08745Sheppo static struct modldrv vswmodldrv = { 2671ae08745Sheppo &mod_driverops, 2681ae08745Sheppo "sun4v Virtual Switch Driver %I%", 2691ae08745Sheppo &vsw_ops, 2701ae08745Sheppo }; 2711ae08745Sheppo 2721ae08745Sheppo #define LDC_ENTER_LOCK(ldcp) \ 2731ae08745Sheppo mutex_enter(&((ldcp)->ldc_cblock));\ 2741ae08745Sheppo mutex_enter(&((ldcp)->ldc_txlock)); 2751ae08745Sheppo #define LDC_EXIT_LOCK(ldcp) \ 2761ae08745Sheppo mutex_exit(&((ldcp)->ldc_txlock));\ 2771ae08745Sheppo mutex_exit(&((ldcp)->ldc_cblock)); 2781ae08745Sheppo 2791ae08745Sheppo /* Driver soft state ptr */ 2801ae08745Sheppo static void *vsw_state; 2811ae08745Sheppo 2821ae08745Sheppo /* 2831ae08745Sheppo * Linked list of "vsw_t" structures - one per instance. 2841ae08745Sheppo */ 2851ae08745Sheppo vsw_t *vsw_head = NULL; 2861ae08745Sheppo krwlock_t vsw_rw; 2871ae08745Sheppo 2881ae08745Sheppo /* 2891ae08745Sheppo * Property names 2901ae08745Sheppo */ 2911ae08745Sheppo static char vdev_propname[] = "virtual-device"; 2921ae08745Sheppo static char vsw_propname[] = "virtual-network-switch"; 2931ae08745Sheppo static char physdev_propname[] = "vsw-phys-dev"; 2941ae08745Sheppo static char smode_propname[] = "vsw-switch-mode"; 2951ae08745Sheppo static char macaddr_propname[] = "local-mac-address"; 2961ae08745Sheppo static char remaddr_propname[] = "remote-mac-address"; 2971ae08745Sheppo static char ldcids_propname[] = "ldc-ids"; 2981ae08745Sheppo static char chan_propname[] = "channel-endpoint"; 2991ae08745Sheppo static char id_propname[] = "id"; 3001ae08745Sheppo static char reg_propname[] = "reg"; 3011ae08745Sheppo 3021ae08745Sheppo /* supported versions */ 3031ae08745Sheppo static ver_sup_t vsw_versions[] = { {1, 0} }; 3041ae08745Sheppo 3051ae08745Sheppo /* 3061ae08745Sheppo * Matching criteria passed to the MDEG to register interest 3071ae08745Sheppo * in changes to 'virtual-device-port' nodes identified by their 3081ae08745Sheppo * 'id' property. 3091ae08745Sheppo */ 3101ae08745Sheppo static md_prop_match_t vport_prop_match[] = { 3111ae08745Sheppo { MDET_PROP_VAL, "id" }, 3121ae08745Sheppo { MDET_LIST_END, NULL } 3131ae08745Sheppo }; 3141ae08745Sheppo 3151ae08745Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port", 3161ae08745Sheppo vport_prop_match }; 3171ae08745Sheppo 3181ae08745Sheppo /* 3191ae08745Sheppo * Specification of an MD node passed to the MDEG to filter any 3201ae08745Sheppo * 'vport' nodes that do not belong to the specified node. This 3211ae08745Sheppo * template is copied for each vsw instance and filled in with 3221ae08745Sheppo * the appropriate 'cfg-handle' value before being passed to the MDEG. 3231ae08745Sheppo */ 3241ae08745Sheppo static mdeg_prop_spec_t vsw_prop_template[] = { 3251ae08745Sheppo { MDET_PROP_STR, "name", vsw_propname }, 3261ae08745Sheppo { MDET_PROP_VAL, "cfg-handle", NULL }, 3271ae08745Sheppo { MDET_LIST_END, NULL, NULL } 3281ae08745Sheppo }; 3291ae08745Sheppo 3301ae08745Sheppo #define VSW_SET_MDEG_PROP_INST(specp, val) (specp)[1].ps_val = (val); 3311ae08745Sheppo 3321ae08745Sheppo /* 3331ae08745Sheppo * Print debug messages - set to 0x1f to enable all msgs 3341ae08745Sheppo * or 0x0 to turn all off. 3351ae08745Sheppo */ 3361ae08745Sheppo int vswdbg = 0x0; 3371ae08745Sheppo 3381ae08745Sheppo /* 3391ae08745Sheppo * debug levels: 3401ae08745Sheppo * 0x01: Function entry/exit tracing 3411ae08745Sheppo * 0x02: Internal function messages 3421ae08745Sheppo * 0x04: Verbose internal messages 3431ae08745Sheppo * 0x08: Warning messages 3441ae08745Sheppo * 0x10: Error messages 3451ae08745Sheppo */ 3461ae08745Sheppo 3471ae08745Sheppo static void 3481ae08745Sheppo vswdebug(vsw_t *vswp, const char *fmt, ...) 3491ae08745Sheppo { 3501ae08745Sheppo char buf[512]; 3511ae08745Sheppo va_list ap; 3521ae08745Sheppo 3531ae08745Sheppo va_start(ap, fmt); 3541ae08745Sheppo (void) vsprintf(buf, fmt, ap); 3551ae08745Sheppo va_end(ap); 3561ae08745Sheppo 3571ae08745Sheppo if (vswp == NULL) 3581ae08745Sheppo cmn_err(CE_CONT, "%s\n", buf); 3591ae08745Sheppo else 3601ae08745Sheppo cmn_err(CE_CONT, "vsw%d: %s\n", vswp->instance, buf); 3611ae08745Sheppo } 3621ae08745Sheppo 3631ae08745Sheppo /* 3641ae08745Sheppo * For the moment the state dump routines have their own 3651ae08745Sheppo * private flag. 3661ae08745Sheppo */ 3671ae08745Sheppo #define DUMP_STATE 0 3681ae08745Sheppo 3691ae08745Sheppo #if DUMP_STATE 3701ae08745Sheppo 3711ae08745Sheppo #define DUMP_TAG(tag) \ 3721ae08745Sheppo { \ 3731ae08745Sheppo D1(NULL, "DUMP_TAG: type 0x%llx", (tag).vio_msgtype); \ 3741ae08745Sheppo D1(NULL, "DUMP_TAG: stype 0x%llx", (tag).vio_subtype); \ 3751ae08745Sheppo D1(NULL, "DUMP_TAG: senv 0x%llx", (tag).vio_subtype_env); \ 3761ae08745Sheppo } 3771ae08745Sheppo 3781ae08745Sheppo #define DUMP_TAG_PTR(tag) \ 3791ae08745Sheppo { \ 3801ae08745Sheppo D1(NULL, "DUMP_TAG: type 0x%llx", (tag)->vio_msgtype); \ 3811ae08745Sheppo D1(NULL, "DUMP_TAG: stype 0x%llx", (tag)->vio_subtype); \ 3821ae08745Sheppo D1(NULL, "DUMP_TAG: senv 0x%llx", (tag)->vio_subtype_env); \ 3831ae08745Sheppo } 3841ae08745Sheppo 3851ae08745Sheppo #define DUMP_FLAGS(flags) dump_flags(flags); 3861ae08745Sheppo #define DISPLAY_STATE() display_state() 3871ae08745Sheppo 3881ae08745Sheppo #else 3891ae08745Sheppo 3901ae08745Sheppo #define DUMP_TAG(tag) 3911ae08745Sheppo #define DUMP_TAG_PTR(tag) 3921ae08745Sheppo #define DUMP_FLAGS(state) 3931ae08745Sheppo #define DISPLAY_STATE() 3941ae08745Sheppo 3951ae08745Sheppo #endif /* DUMP_STATE */ 3961ae08745Sheppo 3971ae08745Sheppo #ifdef DEBUG 3981ae08745Sheppo 3991ae08745Sheppo #define D1 \ 4001ae08745Sheppo if (vswdbg & 0x01) \ 4011ae08745Sheppo vswdebug 4021ae08745Sheppo 4031ae08745Sheppo #define D2 \ 4041ae08745Sheppo if (vswdbg & 0x02) \ 4051ae08745Sheppo vswdebug 4061ae08745Sheppo 4071ae08745Sheppo #define D3 \ 4081ae08745Sheppo if (vswdbg & 0x04) \ 4091ae08745Sheppo vswdebug 4101ae08745Sheppo 4111ae08745Sheppo #define DWARN \ 4121ae08745Sheppo if (vswdbg & 0x08) \ 4131ae08745Sheppo vswdebug 4141ae08745Sheppo 4151ae08745Sheppo #define DERR \ 4161ae08745Sheppo if (vswdbg & 0x10) \ 4171ae08745Sheppo vswdebug 4181ae08745Sheppo 4191ae08745Sheppo #else 4201ae08745Sheppo 4211ae08745Sheppo #define DERR if (0) vswdebug 4221ae08745Sheppo #define DWARN if (0) vswdebug 4231ae08745Sheppo #define D1 if (0) vswdebug 4241ae08745Sheppo #define D2 if (0) vswdebug 4251ae08745Sheppo #define D3 if (0) vswdebug 4261ae08745Sheppo 4271ae08745Sheppo #endif /* DEBUG */ 4281ae08745Sheppo 4291ae08745Sheppo static struct modlinkage modlinkage = { 4301ae08745Sheppo MODREV_1, 4311ae08745Sheppo &vswmodldrv, 4321ae08745Sheppo NULL 4331ae08745Sheppo }; 4341ae08745Sheppo 4351ae08745Sheppo int 4361ae08745Sheppo _init(void) 4371ae08745Sheppo { 4381ae08745Sheppo int status; 4391ae08745Sheppo 4401ae08745Sheppo rw_init(&vsw_rw, NULL, RW_DRIVER, NULL); 4411ae08745Sheppo 4421ae08745Sheppo status = ddi_soft_state_init(&vsw_state, sizeof (vsw_t), 1); 4431ae08745Sheppo if (status != 0) { 4441ae08745Sheppo return (status); 4451ae08745Sheppo } 4461ae08745Sheppo 4471ae08745Sheppo mac_init_ops(&vsw_ops, "vsw"); 4481ae08745Sheppo status = mod_install(&modlinkage); 4491ae08745Sheppo if (status != 0) { 4501ae08745Sheppo ddi_soft_state_fini(&vsw_state); 4511ae08745Sheppo } 4521ae08745Sheppo return (status); 4531ae08745Sheppo } 4541ae08745Sheppo 4551ae08745Sheppo int 4561ae08745Sheppo _fini(void) 4571ae08745Sheppo { 4581ae08745Sheppo int status; 4591ae08745Sheppo 4601ae08745Sheppo status = mod_remove(&modlinkage); 4611ae08745Sheppo if (status != 0) 4621ae08745Sheppo return (status); 4631ae08745Sheppo mac_fini_ops(&vsw_ops); 4641ae08745Sheppo ddi_soft_state_fini(&vsw_state); 4651ae08745Sheppo 4661ae08745Sheppo rw_destroy(&vsw_rw); 4671ae08745Sheppo 4681ae08745Sheppo return (status); 4691ae08745Sheppo } 4701ae08745Sheppo 4711ae08745Sheppo int 4721ae08745Sheppo _info(struct modinfo *modinfop) 4731ae08745Sheppo { 4741ae08745Sheppo return (mod_info(&modlinkage, modinfop)); 4751ae08745Sheppo } 4761ae08745Sheppo 4771ae08745Sheppo static int 4781ae08745Sheppo vsw_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4791ae08745Sheppo { 4801ae08745Sheppo vsw_t *vswp; 4811ae08745Sheppo int smode, instance, i; 4821ae08745Sheppo char hashname[MAXNAMELEN]; 4831ae08745Sheppo char qname[TASKQ_NAMELEN]; 4841ae08745Sheppo int rv = 1; 4851ae08745Sheppo enum { PROG_init = 0x0, PROG_if_lock = 0x1, 4861ae08745Sheppo PROG_fdb = 0x2, PROG_mfdb = 0x4, 4871ae08745Sheppo PROG_report_dev = 0x8, PROG_plist = 0x10, 4881ae08745Sheppo PROG_taskq = 0x20} 4891ae08745Sheppo progress; 4901ae08745Sheppo 4911ae08745Sheppo progress = PROG_init; 4921ae08745Sheppo 4931ae08745Sheppo switch (cmd) { 4941ae08745Sheppo case DDI_ATTACH: 4951ae08745Sheppo break; 4961ae08745Sheppo case DDI_RESUME: 4971ae08745Sheppo /* nothing to do for this non-device */ 4981ae08745Sheppo return (DDI_SUCCESS); 4991ae08745Sheppo case DDI_PM_RESUME: 5001ae08745Sheppo default: 5011ae08745Sheppo return (DDI_FAILURE); 5021ae08745Sheppo } 5031ae08745Sheppo 5041ae08745Sheppo instance = ddi_get_instance(dip); 5051ae08745Sheppo if (ddi_soft_state_zalloc(vsw_state, instance) != DDI_SUCCESS) { 5061ae08745Sheppo DERR(NULL, "vsw%d: ddi_soft_state_zalloc failed", instance); 5071ae08745Sheppo return (DDI_FAILURE); 5081ae08745Sheppo } 5091ae08745Sheppo vswp = ddi_get_soft_state(vsw_state, instance); 5101ae08745Sheppo 5111ae08745Sheppo if (vswp == NULL) { 5121ae08745Sheppo DERR(NULL, "vsw%d: ddi_get_soft_state failed", instance); 5131ae08745Sheppo goto vsw_attach_fail; 5141ae08745Sheppo } 5151ae08745Sheppo 5161ae08745Sheppo vswp->dip = dip; 5171ae08745Sheppo vswp->instance = instance; 5181ae08745Sheppo ddi_set_driver_private(dip, (caddr_t)vswp); 5191ae08745Sheppo 5201ae08745Sheppo rw_init(&vswp->if_lockrw, NULL, RW_DRIVER, NULL); 5211ae08745Sheppo 5221ae08745Sheppo progress |= PROG_if_lock; 5231ae08745Sheppo 5241ae08745Sheppo /* 5251ae08745Sheppo * User specifies (via MD) an array of switching modes in 5261ae08745Sheppo * decreasing order of preference. Default mode is always 5271ae08745Sheppo * layer 2 (mac switching), so init array with that value. 5281ae08745Sheppo */ 5291ae08745Sheppo vswp->smode_idx = 0; 5301ae08745Sheppo for (i = 0; i < NUM_SMODES; i++) 5311ae08745Sheppo vswp->smode[i] = VSW_LAYER2; 5321ae08745Sheppo 5331ae08745Sheppo /* 5341ae08745Sheppo * Get the various properties such as physical device name 5351ae08745Sheppo * (vsw-phys-dev), switch mode etc from the MD. 5361ae08745Sheppo */ 5371ae08745Sheppo vsw_get_md_properties(vswp); 5381ae08745Sheppo 5391ae08745Sheppo /* setup the unicast forwarding database */ 5401ae08745Sheppo (void) snprintf(hashname, MAXNAMELEN, "vsw_unicst_table-%d", 5411ae08745Sheppo vswp->instance); 5421ae08745Sheppo D2(vswp, "creating unicast hash table (%s)...", hashname); 5431ae08745Sheppo vswp->fdb = mod_hash_create_ptrhash(hashname, VSW_NCHAINS, 5441ae08745Sheppo mod_hash_null_valdtor, sizeof (void *)); 5451ae08745Sheppo 5461ae08745Sheppo progress |= PROG_fdb; 5471ae08745Sheppo 5481ae08745Sheppo /* setup the multicast fowarding database */ 5491ae08745Sheppo (void) snprintf(hashname, MAXNAMELEN, "vsw_mcst_table-%d", 5501ae08745Sheppo vswp->instance); 5511ae08745Sheppo D2(vswp, "creating multicast hash table %s)...", hashname); 5521ae08745Sheppo rw_init(&vswp->mfdbrw, NULL, RW_DRIVER, NULL); 5531ae08745Sheppo vswp->mfdb = mod_hash_create_ptrhash(hashname, VSW_NCHAINS, 5541ae08745Sheppo mod_hash_null_valdtor, sizeof (void *)); 5551ae08745Sheppo 5561ae08745Sheppo progress |= PROG_mfdb; 5571ae08745Sheppo 5581ae08745Sheppo /* 5591ae08745Sheppo * create lock protecting list of multicast addresses 5601ae08745Sheppo * which could come via m_multicst() entry point when plumbed. 5611ae08745Sheppo */ 5621ae08745Sheppo mutex_init(&vswp->mca_lock, NULL, MUTEX_DRIVER, NULL); 5631ae08745Sheppo vswp->mcap = NULL; 5641ae08745Sheppo 5651ae08745Sheppo ddi_report_dev(vswp->dip); 5661ae08745Sheppo 5671ae08745Sheppo progress |= PROG_report_dev; 5681ae08745Sheppo 5691ae08745Sheppo WRITE_ENTER(&vsw_rw); 5701ae08745Sheppo vswp->next = vsw_head; 5711ae08745Sheppo vsw_head = vswp; 5721ae08745Sheppo RW_EXIT(&vsw_rw); 5731ae08745Sheppo 5741ae08745Sheppo /* setup the port list */ 5751ae08745Sheppo rw_init(&vswp->plist.lockrw, NULL, RW_DRIVER, NULL); 5761ae08745Sheppo vswp->plist.head = NULL; 5771ae08745Sheppo 5781ae08745Sheppo progress |= PROG_plist; 5791ae08745Sheppo 5801ae08745Sheppo /* 5811ae08745Sheppo * Create the taskq which will process all the VIO 5821ae08745Sheppo * control messages. 5831ae08745Sheppo */ 5841ae08745Sheppo (void) snprintf(qname, TASKQ_NAMELEN, "vsw_taskq%d", vswp->instance); 5851ae08745Sheppo if ((vswp->taskq_p = ddi_taskq_create(vswp->dip, qname, 1, 5861ae08745Sheppo TASKQ_DEFAULTPRI, 0)) == NULL) { 5871ae08745Sheppo cmn_err(CE_WARN, "Unable to create task queue"); 5881ae08745Sheppo goto vsw_attach_fail; 5891ae08745Sheppo } 5901ae08745Sheppo 5911ae08745Sheppo progress |= PROG_taskq; 5921ae08745Sheppo 5931ae08745Sheppo /* select best switching mode */ 5941ae08745Sheppo for (i = 0; i < NUM_SMODES; i++) { 5951ae08745Sheppo smode = vswp->smode[i]; 5961ae08745Sheppo switch (smode) { 5971ae08745Sheppo case VSW_LAYER2: 5981ae08745Sheppo rv = vsw_setup_layer2(vswp); 5991ae08745Sheppo break; 6001ae08745Sheppo 6011ae08745Sheppo case VSW_LAYER2_PROMISC: 6021ae08745Sheppo rv = vsw_setup_layer2(vswp); 6031ae08745Sheppo break; 6041ae08745Sheppo 6051ae08745Sheppo case VSW_LAYER3: 6061ae08745Sheppo rv = vsw_setup_layer3(vswp); 6071ae08745Sheppo break; 6081ae08745Sheppo 6091ae08745Sheppo default: 6101ae08745Sheppo DERR(vswp, "unknown switch mode"); 6111ae08745Sheppo break; 6121ae08745Sheppo } 6131ae08745Sheppo 6141ae08745Sheppo if (rv == 0) { 6151ae08745Sheppo vswp->smode_idx = i; 6161ae08745Sheppo break; 6171ae08745Sheppo } 6181ae08745Sheppo } 6191ae08745Sheppo 6201ae08745Sheppo if (rv == 1) { 6211ae08745Sheppo cmn_err(CE_WARN, "Unable to setup switching mode"); 6221ae08745Sheppo goto vsw_attach_fail; 6231ae08745Sheppo } 6241ae08745Sheppo 6251ae08745Sheppo D2(vswp, "Operating in mode %d", vswp->smode[vswp->smode_idx]); 6261ae08745Sheppo 6271ae08745Sheppo /* 6281ae08745Sheppo * Register with the MAC layer as a network device so 6291ae08745Sheppo * we can be plumbed if desired. 6301ae08745Sheppo * 6311ae08745Sheppo * Do this in both layer 2 and layer 3 mode. 6321ae08745Sheppo */ 6331ae08745Sheppo vswp->if_state &= ~VSW_IF_UP; 6341ae08745Sheppo if (vswp->mdprops & VSW_MD_MACADDR) { 6351ae08745Sheppo if (vsw_mac_register(vswp) != 0) { 6361ae08745Sheppo cmn_err(CE_WARN, "Unable to register as provider " 6371ae08745Sheppo " with MAC layer, continuing with attach"); 6381ae08745Sheppo } 6391ae08745Sheppo } 6401ae08745Sheppo 6411ae08745Sheppo /* 6421ae08745Sheppo * Now we have everything setup, register for MD change 6431ae08745Sheppo * events. 6441ae08745Sheppo */ 6451ae08745Sheppo vsw_mdeg_register(vswp); 6461ae08745Sheppo 6471ae08745Sheppo return (DDI_SUCCESS); 6481ae08745Sheppo 6491ae08745Sheppo vsw_attach_fail: 6501ae08745Sheppo DERR(NULL, "vsw_attach: failed"); 6511ae08745Sheppo 6521ae08745Sheppo if (progress & PROG_taskq) 6531ae08745Sheppo ddi_taskq_destroy(vswp->taskq_p); 6541ae08745Sheppo 6551ae08745Sheppo if (progress & PROG_plist) 6561ae08745Sheppo rw_destroy(&vswp->plist.lockrw); 6571ae08745Sheppo 6581ae08745Sheppo if (progress & PROG_report_dev) { 6591ae08745Sheppo ddi_remove_minor_node(dip, NULL); 6601ae08745Sheppo mutex_destroy(&vswp->mca_lock); 6611ae08745Sheppo } 6621ae08745Sheppo 6631ae08745Sheppo if (progress & PROG_mfdb) { 6641ae08745Sheppo mod_hash_destroy_hash(vswp->mfdb); 6651ae08745Sheppo vswp->mfdb = NULL; 6661ae08745Sheppo rw_destroy(&vswp->mfdbrw); 6671ae08745Sheppo } 6681ae08745Sheppo 6691ae08745Sheppo if (progress & PROG_fdb) { 6701ae08745Sheppo mod_hash_destroy_hash(vswp->fdb); 6711ae08745Sheppo vswp->fdb = NULL; 6721ae08745Sheppo } 6731ae08745Sheppo 6741ae08745Sheppo if (progress & PROG_if_lock) 6751ae08745Sheppo rw_destroy(&vswp->if_lockrw); 6761ae08745Sheppo 6771ae08745Sheppo ddi_soft_state_free(vsw_state, instance); 6781ae08745Sheppo return (DDI_FAILURE); 6791ae08745Sheppo } 6801ae08745Sheppo 6811ae08745Sheppo static int 6821ae08745Sheppo vsw_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6831ae08745Sheppo { 6841ae08745Sheppo vsw_t **vswpp, *vswp; 6851ae08745Sheppo int instance; 6861ae08745Sheppo 6871ae08745Sheppo instance = ddi_get_instance(dip); 6881ae08745Sheppo vswp = ddi_get_soft_state(vsw_state, instance); 6891ae08745Sheppo 6901ae08745Sheppo if (vswp == NULL) { 6911ae08745Sheppo return (DDI_FAILURE); 6921ae08745Sheppo } 6931ae08745Sheppo 6941ae08745Sheppo switch (cmd) { 6951ae08745Sheppo case DDI_DETACH: 6961ae08745Sheppo break; 6971ae08745Sheppo case DDI_SUSPEND: 6981ae08745Sheppo case DDI_PM_SUSPEND: 6991ae08745Sheppo default: 7001ae08745Sheppo return (DDI_FAILURE); 7011ae08745Sheppo } 7021ae08745Sheppo 7031ae08745Sheppo D2(vswp, "detaching instance %d", instance); 7041ae08745Sheppo 7051ae08745Sheppo if (vswp->mdprops & VSW_MD_MACADDR) { 7061ae08745Sheppo if (vsw_mac_unregister(vswp) != 0) { 7071ae08745Sheppo cmn_err(CE_WARN, "Unable to detach from MAC layer"); 7081ae08745Sheppo return (DDI_FAILURE); 7091ae08745Sheppo } 7101ae08745Sheppo } 7111ae08745Sheppo rw_destroy(&vswp->if_lockrw); 7121ae08745Sheppo 7131ae08745Sheppo vsw_mdeg_unregister(vswp); 7141ae08745Sheppo 7151ae08745Sheppo if ((vswp->smode[vswp->smode_idx] == VSW_LAYER2) || 7161ae08745Sheppo (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) { 7171ae08745Sheppo vsw_mac_detach(vswp); 7181ae08745Sheppo } 7191ae08745Sheppo 7201ae08745Sheppo if (vsw_detach_ports(vswp) != 0) { 7211ae08745Sheppo cmn_err(CE_WARN, "Unable to detach ports"); 7221ae08745Sheppo return (DDI_FAILURE); 7231ae08745Sheppo } 7241ae08745Sheppo 7251ae08745Sheppo /* 7261ae08745Sheppo * Remove this instance from any entries it may be on in 7271ae08745Sheppo * the hash table by using the list of addresses maintained 7281ae08745Sheppo * in the vsw_t structure. 7291ae08745Sheppo */ 7301ae08745Sheppo vsw_del_mcst_vsw(vswp); 7311ae08745Sheppo 7321ae08745Sheppo vswp->mcap = NULL; 7331ae08745Sheppo mutex_destroy(&vswp->mca_lock); 7341ae08745Sheppo 7351ae08745Sheppo /* 7361ae08745Sheppo * By now any pending tasks have finished and the underlying 7371ae08745Sheppo * ldc's have been destroyed, so its safe to delete the control 7381ae08745Sheppo * message taskq. 7391ae08745Sheppo */ 7401ae08745Sheppo if (vswp->taskq_p != NULL) 7411ae08745Sheppo ddi_taskq_destroy(vswp->taskq_p); 7421ae08745Sheppo 7431ae08745Sheppo /* 7441ae08745Sheppo * At this stage all the data pointers in the hash table 7451ae08745Sheppo * should be NULL, as all the ports have been removed and will 7461ae08745Sheppo * have deleted themselves from the port lists which the data 7471ae08745Sheppo * pointers point to. Hence we can destroy the table using the 7481ae08745Sheppo * default destructors. 7491ae08745Sheppo */ 7501ae08745Sheppo D2(vswp, "vsw_detach: destroying hash tables.."); 7511ae08745Sheppo mod_hash_destroy_hash(vswp->fdb); 7521ae08745Sheppo vswp->fdb = NULL; 7531ae08745Sheppo 7541ae08745Sheppo WRITE_ENTER(&vswp->mfdbrw); 7551ae08745Sheppo mod_hash_destroy_hash(vswp->mfdb); 7561ae08745Sheppo vswp->mfdb = NULL; 7571ae08745Sheppo RW_EXIT(&vswp->mfdbrw); 7581ae08745Sheppo rw_destroy(&vswp->mfdbrw); 7591ae08745Sheppo 7601ae08745Sheppo ddi_remove_minor_node(dip, NULL); 7611ae08745Sheppo 7621ae08745Sheppo rw_destroy(&vswp->plist.lockrw); 7631ae08745Sheppo WRITE_ENTER(&vsw_rw); 7641ae08745Sheppo for (vswpp = &vsw_head; *vswpp; vswpp = &(*vswpp)->next) { 7651ae08745Sheppo if (*vswpp == vswp) { 7661ae08745Sheppo *vswpp = vswp->next; 7671ae08745Sheppo break; 7681ae08745Sheppo } 7691ae08745Sheppo } 7701ae08745Sheppo RW_EXIT(&vsw_rw); 7711ae08745Sheppo ddi_soft_state_free(vsw_state, instance); 7721ae08745Sheppo 7731ae08745Sheppo return (DDI_SUCCESS); 7741ae08745Sheppo } 7751ae08745Sheppo 7761ae08745Sheppo static int 7771ae08745Sheppo vsw_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 7781ae08745Sheppo { 7791ae08745Sheppo _NOTE(ARGUNUSED(dip)) 7801ae08745Sheppo 7811ae08745Sheppo vsw_t *vswp = NULL; 7821ae08745Sheppo dev_t dev = (dev_t)arg; 7831ae08745Sheppo int instance; 7841ae08745Sheppo 7851ae08745Sheppo instance = getminor(dev); 7861ae08745Sheppo 7871ae08745Sheppo switch (infocmd) { 7881ae08745Sheppo case DDI_INFO_DEVT2DEVINFO: 7891ae08745Sheppo if ((vswp = ddi_get_soft_state(vsw_state, instance)) == NULL) { 7901ae08745Sheppo *result = NULL; 7911ae08745Sheppo return (DDI_FAILURE); 7921ae08745Sheppo } 7931ae08745Sheppo *result = vswp->dip; 7941ae08745Sheppo return (DDI_SUCCESS); 7951ae08745Sheppo 7961ae08745Sheppo case DDI_INFO_DEVT2INSTANCE: 7971ae08745Sheppo *result = (void *)(uintptr_t)instance; 7981ae08745Sheppo return (DDI_SUCCESS); 7991ae08745Sheppo 8001ae08745Sheppo default: 8011ae08745Sheppo *result = NULL; 8021ae08745Sheppo return (DDI_FAILURE); 8031ae08745Sheppo } 8041ae08745Sheppo } 8051ae08745Sheppo 8061ae08745Sheppo /* 8071ae08745Sheppo * Get the properties from our MD node. 8081ae08745Sheppo */ 8091ae08745Sheppo static void 8101ae08745Sheppo vsw_get_md_properties(vsw_t *vswp) 8111ae08745Sheppo { 8121ae08745Sheppo md_t *mdp = NULL; 8131ae08745Sheppo int num_nodes = 0; 8141ae08745Sheppo int len = 0, listsz = 0; 8151ae08745Sheppo int num_vdev = 0; 8161ae08745Sheppo int i, idx; 8171ae08745Sheppo boolean_t found_node = B_FALSE; 8181ae08745Sheppo char *smode = NULL; 8191ae08745Sheppo char *curr_mode = NULL; 8201ae08745Sheppo char *physname = NULL; 8211ae08745Sheppo char *node_name = NULL; 8221ae08745Sheppo char *dev; 8231ae08745Sheppo uint64_t macaddr = 0; 8241ae08745Sheppo uint64_t md_inst, obp_inst; 8251ae08745Sheppo mde_cookie_t *listp = NULL; 8261ae08745Sheppo mde_cookie_t rootnode; 8271ae08745Sheppo 8281ae08745Sheppo D1(vswp, "%s: enter", __func__); 8291ae08745Sheppo 8301ae08745Sheppo /* 8311ae08745Sheppo * Further down we compare the obp 'reg' property to the 8321ae08745Sheppo * 'cfg-handle' property in the vsw MD node to determine 8331ae08745Sheppo * if the node refers to this particular instance. So if 8341ae08745Sheppo * we can't read the obp value then there is no point 8351ae08745Sheppo * in proceeding further. 8361ae08745Sheppo */ 8371ae08745Sheppo if (ddi_prop_exists(DDI_DEV_T_ANY, vswp->dip, 8381ae08745Sheppo DDI_PROP_DONTPASS, reg_propname) != 1) { 8391ae08745Sheppo cmn_err(CE_WARN, "Unable to read %s property " 8401ae08745Sheppo "from OBP device node", reg_propname); 8411ae08745Sheppo return; 8421ae08745Sheppo } 8431ae08745Sheppo 8441ae08745Sheppo obp_inst = ddi_prop_get_int(DDI_DEV_T_ANY, vswp->dip, 8451ae08745Sheppo DDI_PROP_DONTPASS, reg_propname, 0); 8461ae08745Sheppo 8471ae08745Sheppo D2(vswp, "%s: obp_inst 0x%llx", __func__, obp_inst); 8481ae08745Sheppo 8491ae08745Sheppo if ((mdp = md_get_handle()) == NULL) { 8501ae08745Sheppo DERR(vswp, "%s: unable to init MD", __func__); 8511ae08745Sheppo return; 8521ae08745Sheppo } 8531ae08745Sheppo 8541ae08745Sheppo if ((num_nodes = md_node_count(mdp)) <= 0) { 8551ae08745Sheppo DERR(vswp, "%s: invalid number of nodes found %d", 8561ae08745Sheppo __func__, num_nodes); 8571ae08745Sheppo (void) md_fini_handle(mdp); 8581ae08745Sheppo return; 8591ae08745Sheppo } 8601ae08745Sheppo 8611ae08745Sheppo D2(vswp, "%s: %d nodes in total in MD", __func__, num_nodes); 8621ae08745Sheppo 8631ae08745Sheppo /* allocate enough space for node list */ 8641ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 8651ae08745Sheppo listp = kmem_zalloc(listsz, KM_SLEEP); 8661ae08745Sheppo 8671ae08745Sheppo rootnode = md_root_node(mdp); 8681ae08745Sheppo 8691ae08745Sheppo /* Get the list of virtual devices */ 8701ae08745Sheppo num_vdev = md_scan_dag(mdp, rootnode, 8711ae08745Sheppo md_find_name(mdp, vdev_propname), 8721ae08745Sheppo md_find_name(mdp, "fwd"), listp); 8731ae08745Sheppo 8741ae08745Sheppo if (num_vdev <= 0) { 8751ae08745Sheppo DERR(vswp, "%s: didn't find any virtual-device nodes in MD", 8761ae08745Sheppo __func__); 8771ae08745Sheppo goto md_prop_exit; 8781ae08745Sheppo } 8791ae08745Sheppo 8801ae08745Sheppo D2(vswp, "%s: %d virtual-device nodes found", __func__, num_vdev); 8811ae08745Sheppo 8821ae08745Sheppo /* Look for the virtual switch nodes in the list */ 8831ae08745Sheppo for (idx = 0; idx < num_vdev; idx++) { 8841ae08745Sheppo if (md_get_prop_str(mdp, listp[idx], 8851ae08745Sheppo "name", &node_name) != 0) { 8861ae08745Sheppo DERR(vswp, "%s: unable to get node name", __func__); 8871ae08745Sheppo continue; 8881ae08745Sheppo 8891ae08745Sheppo } 8901ae08745Sheppo 8911ae08745Sheppo if (strcmp(node_name, vsw_propname) == 0) { 8921ae08745Sheppo /* Virtual switch node */ 8931ae08745Sheppo if (md_get_prop_val(mdp, listp[idx], 8941ae08745Sheppo "cfg-handle", &md_inst) != 0) { 8951ae08745Sheppo DERR(vswp, "%s: unable to get cfg-handle from" 8961ae08745Sheppo " node %d", __func__, idx); 8971ae08745Sheppo goto md_prop_exit; 8981ae08745Sheppo } else if (md_inst == obp_inst) { 8991ae08745Sheppo D2(vswp, "%s: found matching node (%d)" 9001ae08745Sheppo " 0x%llx == 0x%llx", __func__, idx, 9011ae08745Sheppo md_inst, obp_inst); 9021ae08745Sheppo found_node = B_TRUE; 9031ae08745Sheppo break; 9041ae08745Sheppo } 9051ae08745Sheppo } 9061ae08745Sheppo } 9071ae08745Sheppo 9081ae08745Sheppo if (!found_node) { 9091ae08745Sheppo DWARN(vswp, "%s: couldn't find correct vsw node", __func__); 9101ae08745Sheppo goto md_prop_exit; 9111ae08745Sheppo } 9121ae08745Sheppo 9131ae08745Sheppo /* 9141ae08745Sheppo * Now, having found the correct node, get the various properties. 9151ae08745Sheppo */ 9161ae08745Sheppo 9171ae08745Sheppo if (md_get_prop_data(mdp, listp[idx], physdev_propname, 9181ae08745Sheppo (uint8_t **)(&physname), &len) != 0) { 9191ae08745Sheppo cmn_err(CE_WARN, "%s: unable to get name(s) of physical " 9201ae08745Sheppo "device(s) from MD", __func__); 9211ae08745Sheppo } else if ((strlen(physname) + 1) > LIFNAMSIZ) { 9221ae08745Sheppo cmn_err(CE_WARN, "%s is too long a device name", physname); 9231ae08745Sheppo } else { 9241ae08745Sheppo (void) strncpy(vswp->physname, physname, strlen(physname) + 1); 9251ae08745Sheppo vswp->mdprops |= VSW_MD_PHYSNAME; 9261ae08745Sheppo D2(vswp, "%s: using first device specified (%s)", 9271ae08745Sheppo __func__, vswp->physname); 9281ae08745Sheppo } 9291ae08745Sheppo 9301ae08745Sheppo 9311ae08745Sheppo #ifdef DEBUG 9321ae08745Sheppo /* 9331ae08745Sheppo * As a temporary measure to aid testing we check to see if there 9341ae08745Sheppo * is a vsw.conf file present. If there is we use the value of the 9351ae08745Sheppo * vsw_physname property in the file as the name of the physical 9361ae08745Sheppo * device, overriding the value from the MD. 9371ae08745Sheppo * 9381ae08745Sheppo * There may be multiple devices listed, but for the moment 9391ae08745Sheppo * we just use the first one. 9401ae08745Sheppo */ 9411ae08745Sheppo if (ddi_prop_lookup_string(DDI_DEV_T_ANY, vswp->dip, 0, 9421ae08745Sheppo "vsw_physname", &dev) == DDI_PROP_SUCCESS) { 9431ae08745Sheppo if ((strlen(dev) + 1) > LIFNAMSIZ) { 9441ae08745Sheppo cmn_err(CE_WARN, "%s is too long a device name", dev); 9451ae08745Sheppo } else { 9461ae08745Sheppo cmn_err(CE_NOTE, "%s: using device name (%s) from " 9471ae08745Sheppo "config file", __func__, dev); 9481ae08745Sheppo 9491ae08745Sheppo (void) strncpy(vswp->physname, dev, strlen(dev) + 1); 9501ae08745Sheppo vswp->mdprops |= VSW_MD_PHYSNAME; 9511ae08745Sheppo } 9521ae08745Sheppo 9531ae08745Sheppo ddi_prop_free(dev); 9541ae08745Sheppo 9551ae08745Sheppo } 9561ae08745Sheppo #endif 9571ae08745Sheppo 9581ae08745Sheppo /* local mac address */ 9591ae08745Sheppo if (md_get_prop_val(mdp, listp[idx], 9601ae08745Sheppo macaddr_propname, &macaddr) != 0) { 9611ae08745Sheppo cmn_err(CE_WARN, "%s: unable to get local MAC address", 9621ae08745Sheppo __func__); 9631ae08745Sheppo } else { 9641ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 9651ae08745Sheppo for (i = ETHERADDRL - 1; i >= 0; i--) { 9661ae08745Sheppo vswp->if_addr.ether_addr_octet[i] = macaddr & 0xFF; 9671ae08745Sheppo macaddr >>= 8; 9681ae08745Sheppo } 9691ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 9701ae08745Sheppo vswp->mdprops |= VSW_MD_MACADDR; 9711ae08745Sheppo } 9721ae08745Sheppo 9731ae08745Sheppo /* 9741ae08745Sheppo * Get the switch-mode property. The modes are listed in 9751ae08745Sheppo * decreasing order of preference, i.e. prefered mode is 9761ae08745Sheppo * first item in list. 9771ae08745Sheppo */ 9781ae08745Sheppo len = 0; 9791ae08745Sheppo if (md_get_prop_data(mdp, listp[idx], smode_propname, 9801ae08745Sheppo (uint8_t **)(&smode), &len) != 0) { 9811ae08745Sheppo /* 9821ae08745Sheppo * Unable to get switch-mode property, so just use 9831ae08745Sheppo * default values which vswp->smode[] array has already 9841ae08745Sheppo * been pre-populated with, namely layer2. 9851ae08745Sheppo */ 9861ae08745Sheppo cmn_err(CE_WARN, "%s: unable to get switch mode property, " 9871ae08745Sheppo "defaulting to layer 2 mode", __func__); 9881ae08745Sheppo } else { 9891ae08745Sheppo i = 0; 9901ae08745Sheppo curr_mode = smode; 9911ae08745Sheppo /* 9921ae08745Sheppo * Modes of operation: 9931ae08745Sheppo * 'switched' - layer 2 switching, underlying HW in 9941ae08745Sheppo * non-promiscuous mode. 9951ae08745Sheppo * 'promiscuous' - layer 2 switching, underlying HW in 9961ae08745Sheppo * promiscuous mode. 9971ae08745Sheppo * 'routed' - layer 3 (i.e. IP) routing, underlying HW 9981ae08745Sheppo * in non-promiscuous mode. 9991ae08745Sheppo */ 10001ae08745Sheppo while ((curr_mode < (smode + len)) && (i < NUM_SMODES)) { 10011ae08745Sheppo D2(vswp, "%s: curr_mode = [%s]", __func__, curr_mode); 10021ae08745Sheppo if (strcmp(curr_mode, "switched") == 0) 10031ae08745Sheppo vswp->smode[i] = VSW_LAYER2; 10041ae08745Sheppo else if (strcmp(curr_mode, "promiscuous") == 0) 10051ae08745Sheppo vswp->smode[i] = VSW_LAYER2_PROMISC; 10061ae08745Sheppo else if (strcmp(curr_mode, "routed") == 0) 10071ae08745Sheppo vswp->smode[i] = VSW_LAYER3; 10081ae08745Sheppo else { 10091ae08745Sheppo DERR(vswp, "%s: unknown mode %s", 10101ae08745Sheppo __func__, curr_mode); 10111ae08745Sheppo /* default to layer 2 */ 10121ae08745Sheppo vswp->smode[i] = VSW_LAYER2; 10131ae08745Sheppo } 10141ae08745Sheppo curr_mode += strlen(curr_mode) + 1; 10151ae08745Sheppo i++; 10161ae08745Sheppo } 10171ae08745Sheppo 10181ae08745Sheppo vswp->mdprops |= VSW_MD_SMODE; 10191ae08745Sheppo } 10201ae08745Sheppo 10211ae08745Sheppo md_prop_exit: 10221ae08745Sheppo (void) md_fini_handle(mdp); 10231ae08745Sheppo 10241ae08745Sheppo kmem_free(listp, listsz); 10251ae08745Sheppo 10261ae08745Sheppo D1(vswp, "%s: exit", __func__); 10271ae08745Sheppo } 10281ae08745Sheppo 10291ae08745Sheppo static int 10301ae08745Sheppo vsw_setup_layer2(vsw_t *vswp) 10311ae08745Sheppo { 10321ae08745Sheppo int rv = 0; 10331ae08745Sheppo 10341ae08745Sheppo D1(vswp, "%s: enter", __func__); 10351ae08745Sheppo 10361ae08745Sheppo vsw_switch_frame = vsw_switch_l2_frame; 10371ae08745Sheppo 10381ae08745Sheppo /* 10391ae08745Sheppo * Attempt to link into the MAC layer so we can get 10401ae08745Sheppo * and send packets out over the physical adapter. 10411ae08745Sheppo */ 10421ae08745Sheppo if (vswp->mdprops & VSW_MD_PHYSNAME) { 10431ae08745Sheppo if (vsw_mac_attach(vswp) != 0) { 10441ae08745Sheppo /* 10451ae08745Sheppo * Registration with the MAC layer has failed, 10461ae08745Sheppo * so return 1 so that can fall back to next 10471ae08745Sheppo * prefered switching method. 10481ae08745Sheppo */ 10491ae08745Sheppo cmn_err(CE_WARN, "!unable to join as MAC layer " 10501ae08745Sheppo "client, continuing with attach"); 10511ae08745Sheppo rv = 1; 10521ae08745Sheppo } 10531ae08745Sheppo } else { 10541ae08745Sheppo /* No physical device name found in MD */ 10551ae08745Sheppo DERR(vswp, "%s: no physical device name specified", __func__); 10561ae08745Sheppo rv = 1; 10571ae08745Sheppo } 10581ae08745Sheppo 10591ae08745Sheppo D1(vswp, "%s: exit", __func__); 10601ae08745Sheppo 10611ae08745Sheppo return (rv); 10621ae08745Sheppo } 10631ae08745Sheppo 10641ae08745Sheppo static int 10651ae08745Sheppo vsw_setup_layer3(vsw_t *vswp) 10661ae08745Sheppo { 10671ae08745Sheppo D1(vswp, "%s: enter", __func__); 10681ae08745Sheppo 10691ae08745Sheppo D2(vswp, "%s: operating in layer 3 mode", __func__); 10701ae08745Sheppo vsw_switch_frame = vsw_switch_l3_frame; 10711ae08745Sheppo 10721ae08745Sheppo D1(vswp, "%s: exit", __func__); 10731ae08745Sheppo 10741ae08745Sheppo return (0); 10751ae08745Sheppo } 10761ae08745Sheppo 10771ae08745Sheppo /* 10781ae08745Sheppo * Link into the MAC layer to gain access to the services provided by 10791ae08745Sheppo * the underlying physical device driver (which should also have 10801ae08745Sheppo * registered with the MAC layer). 10811ae08745Sheppo * 10821ae08745Sheppo * Only when in layer 2 mode. 10831ae08745Sheppo */ 10841ae08745Sheppo static int 10851ae08745Sheppo vsw_mac_attach(vsw_t *vswp) 10861ae08745Sheppo { 1087*ba2e4443Sseb char drv[LIFNAMSIZ]; 1088*ba2e4443Sseb uint_t ddi_instance; 1089*ba2e4443Sseb 10901ae08745Sheppo D1(vswp, "vsw_mac_attach: enter"); 10911ae08745Sheppo 10921ae08745Sheppo vswp->mh = NULL; 10931ae08745Sheppo vswp->mrh = NULL; 10941ae08745Sheppo vswp->mnh = NULL; 10951ae08745Sheppo 10961ae08745Sheppo ASSERT(vswp->mdprops & VSW_MD_PHYSNAME); 10971ae08745Sheppo 1098*ba2e4443Sseb if (ddi_parse(vswp->physname, drv, &ddi_instance) != DDI_SUCCESS) { 1099*ba2e4443Sseb cmn_err(CE_WARN, "invalid device name: %s", vswp->physname); 1100*ba2e4443Sseb goto mac_fail_exit; 1101*ba2e4443Sseb } 1102*ba2e4443Sseb if ((mac_open(vswp->physname, ddi_instance, &vswp->mh)) != 0) { 11031ae08745Sheppo cmn_err(CE_WARN, "mac_open %s failed", vswp->physname); 11041ae08745Sheppo goto mac_fail_exit; 11051ae08745Sheppo } 11061ae08745Sheppo 11071ae08745Sheppo D2(vswp, "vsw_mac_attach: using device %s", vswp->physname); 11081ae08745Sheppo 11091ae08745Sheppo /* register for changes in the interface */ 11101ae08745Sheppo vswp->mnh = mac_notify_add(vswp->mh, vsw_notify_cb, (void *)vswp); 11111ae08745Sheppo 11121ae08745Sheppo /* register our rx callback function */ 11131ae08745Sheppo vswp->mrh = mac_rx_add(vswp->mh, vsw_rx_cb, (void *)vswp); 11141ae08745Sheppo 11151ae08745Sheppo /* get the MAC tx fn */ 11161ae08745Sheppo vswp->txinfo = mac_tx_get(vswp->mh); 11171ae08745Sheppo 11181ae08745Sheppo /* start the interface */ 11191ae08745Sheppo if (mac_start(vswp->mh) != 0) { 11201ae08745Sheppo cmn_err(CE_WARN, "could not start mac interface"); 11211ae08745Sheppo goto mac_fail_exit; 11221ae08745Sheppo } 11231ae08745Sheppo 11241ae08745Sheppo /* get and store original promisc setting */ 11251ae08745Sheppo vswp->init_promisc = mac_promisc_get(vswp->mh, MAC_DEVPROMISC); 11261ae08745Sheppo 11271ae08745Sheppo /* 11281ae08745Sheppo * FUTURE: When we have the ability to set multiple unicast 11291ae08745Sheppo * mac address then we won't have to set the device into 11301ae08745Sheppo * promisc mode, but for the moment its the only way we. 11311ae08745Sheppo * can see pkts that logical domains we are serving are 11321ae08745Sheppo * interested in. 11331ae08745Sheppo */ 11341ae08745Sheppo if ((vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC) && 11351ae08745Sheppo (vswp->init_promisc == B_FALSE)) { 11361ae08745Sheppo DERR(vswp, "vsw_mac_attach: enabling promisc mode.."); 11371ae08745Sheppo 11381ae08745Sheppo if (mac_promisc_set(vswp->mh, B_TRUE, MAC_DEVPROMISC) != 0) { 11391ae08745Sheppo DERR(vswp, "vsw_mac_attach: unable to set device" 11401ae08745Sheppo " into promiscuous mode"); 11411ae08745Sheppo goto mac_fail_exit; 11421ae08745Sheppo } 11431ae08745Sheppo } 11441ae08745Sheppo 11451ae08745Sheppo D1(vswp, "vsw_mac_attach: exit"); 11461ae08745Sheppo return (0); 11471ae08745Sheppo 11481ae08745Sheppo mac_fail_exit: 11491ae08745Sheppo if (vswp->mh != NULL) { 11501ae08745Sheppo mac_promisc_set(vswp->mh, vswp->init_promisc, MAC_DEVPROMISC); 11511ae08745Sheppo if (vswp->mrh != NULL) 11521ae08745Sheppo mac_rx_remove(vswp->mh, vswp->mrh); 11531ae08745Sheppo 11541ae08745Sheppo if (vswp->mnh != NULL) 11551ae08745Sheppo mac_notify_remove(vswp->mh, vswp->mnh); 11561ae08745Sheppo 11571ae08745Sheppo mac_close(vswp->mh); 11581ae08745Sheppo } 11591ae08745Sheppo 11601ae08745Sheppo vswp->mrh = NULL; 11611ae08745Sheppo vswp->mnh = NULL; 11621ae08745Sheppo vswp->mh = NULL; 11631ae08745Sheppo vswp->txinfo = NULL; 11641ae08745Sheppo 11651ae08745Sheppo D1(vswp, "vsw_mac_attach: fail exit"); 11661ae08745Sheppo return (1); 11671ae08745Sheppo } 11681ae08745Sheppo 11691ae08745Sheppo static void 11701ae08745Sheppo vsw_mac_detach(vsw_t *vswp) 11711ae08745Sheppo { 11721ae08745Sheppo D1(vswp, "vsw_mac_detach: enter"); 11731ae08745Sheppo 11741ae08745Sheppo if (vswp->mh != NULL) { 11751ae08745Sheppo /* restore promisc to original setting */ 11761ae08745Sheppo mac_promisc_set(vswp->mh, vswp->init_promisc, MAC_DEVPROMISC); 11771ae08745Sheppo if (vswp->mrh != NULL) 11781ae08745Sheppo mac_rx_remove(vswp->mh, vswp->mrh); 11791ae08745Sheppo 11801ae08745Sheppo if (vswp->mnh != NULL) 11811ae08745Sheppo mac_notify_remove(vswp->mh, vswp->mnh); 11821ae08745Sheppo 11831ae08745Sheppo mac_close(vswp->mh); 11841ae08745Sheppo } 11851ae08745Sheppo 11861ae08745Sheppo vswp->mrh = NULL; 11871ae08745Sheppo vswp->mnh = NULL; 11881ae08745Sheppo vswp->mh = NULL; 11891ae08745Sheppo vswp->txinfo = NULL; 11901ae08745Sheppo 11911ae08745Sheppo D1(vswp, "vsw_mac_detach: exit"); 11921ae08745Sheppo } 11931ae08745Sheppo 11941ae08745Sheppo /* 11951ae08745Sheppo * Get notified of changes to the interface. 11961ae08745Sheppo * 11971ae08745Sheppo * For the moment we brute force the interface back 11981ae08745Sheppo * into promisc mode if it is unset (e.g. by snoop). 11991ae08745Sheppo * When we have the ability to set multiple mac addresses, 12001ae08745Sheppo * we will need to see if this is necessary. 12011ae08745Sheppo */ 12021ae08745Sheppo static void 12031ae08745Sheppo vsw_notify_cb(void *arg, mac_notify_type_t type) 12041ae08745Sheppo { 12051ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 12061ae08745Sheppo 12071ae08745Sheppo switch (type) { 12081ae08745Sheppo case MAC_NOTE_PROMISC: 12091ae08745Sheppo vswp->txinfo = mac_tx_get(vswp->mh); 12101ae08745Sheppo if (mac_promisc_get(vswp->mh, MAC_DEVPROMISC) == B_TRUE) { 12111ae08745Sheppo D2(vswp, "%s: still in PROMISC mode", __func__); 12121ae08745Sheppo } else { 12131ae08745Sheppo D2(vswp, "%s: now in NON-PROMISC mode", __func__); 12141ae08745Sheppo D2(vswp, "...re-enabling"); 12151ae08745Sheppo mac_promisc_set(vswp->mh, B_TRUE, MAC_DEVPROMISC); 12161ae08745Sheppo } 12171ae08745Sheppo break; 12181ae08745Sheppo default: 12191ae08745Sheppo break; 12201ae08745Sheppo } 12211ae08745Sheppo } 12221ae08745Sheppo 12231ae08745Sheppo /* 12241ae08745Sheppo * receive callback routine. Invoked by MAC layer when there 12251ae08745Sheppo * are pkts being passed up from physical device. 12261ae08745Sheppo * 12271ae08745Sheppo * PERF: It may be more efficient when the card is in promisc 12281ae08745Sheppo * mode to check the dest address of the pkts here (against 12291ae08745Sheppo * the FDB) rather than checking later. Needs to be investigated. 12301ae08745Sheppo */ 12311ae08745Sheppo static void 12321ae08745Sheppo vsw_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp) 12331ae08745Sheppo { 12341ae08745Sheppo _NOTE(ARGUNUSED(mrh)) 12351ae08745Sheppo 12361ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 12371ae08745Sheppo 12381ae08745Sheppo ASSERT(vswp != NULL); 12391ae08745Sheppo 12401ae08745Sheppo D1(vswp, "vsw_rx_cb: enter"); 12411ae08745Sheppo 12421ae08745Sheppo /* switch the chain of packets received */ 12431ae08745Sheppo vsw_switch_frame(vswp, mp, VSW_PHYSDEV, NULL, NULL); 12441ae08745Sheppo 12451ae08745Sheppo D1(vswp, "vsw_rx_cb: exit"); 12461ae08745Sheppo } 12471ae08745Sheppo 12481ae08745Sheppo /* 12491ae08745Sheppo * Send a message out over the physical device via the MAC layer. 12501ae08745Sheppo * 12511ae08745Sheppo * Returns any mblks that it was unable to transmit. 12521ae08745Sheppo */ 12531ae08745Sheppo static mblk_t * 12541ae08745Sheppo vsw_tx_msg(vsw_t *vswp, mblk_t *mp) 12551ae08745Sheppo { 12561ae08745Sheppo const mac_txinfo_t *mtp; 12571ae08745Sheppo mblk_t *nextp; 12581ae08745Sheppo 12591ae08745Sheppo if (vswp->mh == NULL) { 12601ae08745Sheppo DERR(vswp, "vsw_tx_msg: dropping pkts: no tx routine avail"); 12611ae08745Sheppo return (mp); 12621ae08745Sheppo } else { 12631ae08745Sheppo for (;;) { 12641ae08745Sheppo nextp = mp->b_next; 12651ae08745Sheppo mp->b_next = NULL; 12661ae08745Sheppo 12671ae08745Sheppo mtp = vswp->txinfo; 12681ae08745Sheppo if ((mp = mtp->mt_fn(mtp->mt_arg, mp)) != NULL) { 12691ae08745Sheppo mp->b_next = nextp; 12701ae08745Sheppo break; 12711ae08745Sheppo } 12721ae08745Sheppo 12731ae08745Sheppo if ((mp = nextp) == NULL) 12741ae08745Sheppo break; 12751ae08745Sheppo 12761ae08745Sheppo } 12771ae08745Sheppo 12781ae08745Sheppo } 12791ae08745Sheppo 12801ae08745Sheppo return (mp); 12811ae08745Sheppo } 12821ae08745Sheppo 12831ae08745Sheppo /* 12841ae08745Sheppo * Register with the MAC layer as a network device, so we 12851ae08745Sheppo * can be plumbed if necessary. 12861ae08745Sheppo */ 12871ae08745Sheppo static int 12881ae08745Sheppo vsw_mac_register(vsw_t *vswp) 12891ae08745Sheppo { 1290*ba2e4443Sseb mac_register_t *macp; 1291*ba2e4443Sseb int rv; 12921ae08745Sheppo 12931ae08745Sheppo D1(vswp, "%s: enter", __func__); 12941ae08745Sheppo 1295*ba2e4443Sseb if ((macp = mac_alloc(MAC_VERSION)) == NULL) 1296*ba2e4443Sseb return (EINVAL); 1297*ba2e4443Sseb macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 12981ae08745Sheppo macp->m_driver = vswp; 1299*ba2e4443Sseb macp->m_dip = vswp->dip; 1300*ba2e4443Sseb macp->m_src_addr = (uint8_t *)&vswp->if_addr; 1301*ba2e4443Sseb macp->m_callbacks = &vsw_m_callbacks; 1302*ba2e4443Sseb macp->m_min_sdu = 0; 1303*ba2e4443Sseb macp->m_max_sdu = ETHERMTU; 1304*ba2e4443Sseb rv = mac_register(macp, &vswp->if_mh); 1305*ba2e4443Sseb mac_free(macp); 1306*ba2e4443Sseb if (rv == 0) 1307*ba2e4443Sseb vswp->if_state |= VSW_IF_REG; 13081ae08745Sheppo 13091ae08745Sheppo D1(vswp, "%s: exit", __func__); 13101ae08745Sheppo 13111ae08745Sheppo return (rv); 13121ae08745Sheppo } 13131ae08745Sheppo 13141ae08745Sheppo static int 13151ae08745Sheppo vsw_mac_unregister(vsw_t *vswp) 13161ae08745Sheppo { 13171ae08745Sheppo int rv = 0; 13181ae08745Sheppo 13191ae08745Sheppo D1(vswp, "%s: enter", __func__); 13201ae08745Sheppo 13211ae08745Sheppo WRITE_ENTER(&vswp->if_lockrw); 13221ae08745Sheppo 1323*ba2e4443Sseb if (vswp->if_state & VSW_IF_REG) { 1324*ba2e4443Sseb rv = mac_unregister(vswp->if_mh); 13251ae08745Sheppo if (rv != 0) { 13261ae08745Sheppo DWARN(vswp, "%s: unable to unregister from MAC " 13271ae08745Sheppo "framework", __func__); 13281ae08745Sheppo 13291ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 13301ae08745Sheppo D1(vswp, "%s: fail exit", __func__); 13311ae08745Sheppo return (rv); 13321ae08745Sheppo } 13331ae08745Sheppo 1334*ba2e4443Sseb /* mark i/f as down and unregistered */ 1335*ba2e4443Sseb vswp->if_state &= ~(VSW_IF_UP | VSW_IF_REG); 13361ae08745Sheppo } 13371ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 13381ae08745Sheppo 13391ae08745Sheppo D1(vswp, "%s: exit", __func__); 13401ae08745Sheppo 13411ae08745Sheppo return (rv); 13421ae08745Sheppo } 13431ae08745Sheppo 1344*ba2e4443Sseb static int 1345*ba2e4443Sseb vsw_m_stat(void *arg, uint_t stat, uint64_t *val) 13461ae08745Sheppo { 13471ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 13481ae08745Sheppo 13491ae08745Sheppo D1(vswp, "%s: enter", __func__); 13501ae08745Sheppo 1351*ba2e4443Sseb if (vswp->mh == NULL) 1352*ba2e4443Sseb return (EINVAL); 13531ae08745Sheppo 13541ae08745Sheppo /* return stats from underlying device */ 1355*ba2e4443Sseb *val = mac_stat_get(vswp->mh, stat); 1356*ba2e4443Sseb return (0); 13571ae08745Sheppo } 13581ae08745Sheppo 13591ae08745Sheppo static void 13601ae08745Sheppo vsw_m_stop(void *arg) 13611ae08745Sheppo { 13621ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 13631ae08745Sheppo 13641ae08745Sheppo D1(vswp, "%s: enter", __func__); 13651ae08745Sheppo 13661ae08745Sheppo WRITE_ENTER(&vswp->if_lockrw); 13671ae08745Sheppo vswp->if_state &= ~VSW_IF_UP; 13681ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 13691ae08745Sheppo 13701ae08745Sheppo D1(vswp, "%s: exit (state = %d)", __func__, vswp->if_state); 13711ae08745Sheppo } 13721ae08745Sheppo 13731ae08745Sheppo static int 13741ae08745Sheppo vsw_m_start(void *arg) 13751ae08745Sheppo { 13761ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 13771ae08745Sheppo 13781ae08745Sheppo D1(vswp, "%s: enter", __func__); 13791ae08745Sheppo 13801ae08745Sheppo WRITE_ENTER(&vswp->if_lockrw); 13811ae08745Sheppo vswp->if_state |= VSW_IF_UP; 13821ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 13831ae08745Sheppo 13841ae08745Sheppo D1(vswp, "%s: exit (state = %d)", __func__, vswp->if_state); 13851ae08745Sheppo return (0); 13861ae08745Sheppo } 13871ae08745Sheppo 13881ae08745Sheppo /* 13891ae08745Sheppo * Change the local interface address. 13901ae08745Sheppo */ 13911ae08745Sheppo static int 13921ae08745Sheppo vsw_m_unicst(void *arg, const uint8_t *macaddr) 13931ae08745Sheppo { 13941ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 13951ae08745Sheppo 13961ae08745Sheppo D1(vswp, "%s: enter", __func__); 13971ae08745Sheppo 13981ae08745Sheppo WRITE_ENTER(&vswp->if_lockrw); 13991ae08745Sheppo ether_copy(macaddr, &vswp->if_addr); 14001ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 14011ae08745Sheppo 14021ae08745Sheppo D1(vswp, "%s: exit", __func__); 14031ae08745Sheppo 14041ae08745Sheppo return (0); 14051ae08745Sheppo } 14061ae08745Sheppo 14071ae08745Sheppo static int 14081ae08745Sheppo vsw_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 14091ae08745Sheppo { 14101ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 14111ae08745Sheppo mcst_addr_t *mcst_p = NULL; 14121ae08745Sheppo uint64_t addr = 0x0; 14131ae08745Sheppo int i; 14141ae08745Sheppo 14151ae08745Sheppo D1(vswp, "%s: enter", __func__); 14161ae08745Sheppo 14171ae08745Sheppo /* 14181ae08745Sheppo * Convert address into form that can be used 14191ae08745Sheppo * as hash table key. 14201ae08745Sheppo */ 14211ae08745Sheppo for (i = 0; i < ETHERADDRL; i++) { 14221ae08745Sheppo addr = (addr << 8) | mca[i]; 14231ae08745Sheppo } 14241ae08745Sheppo 14251ae08745Sheppo D2(vswp, "%s: addr = 0x%llx", __func__, addr); 14261ae08745Sheppo 14271ae08745Sheppo if (add) { 14281ae08745Sheppo D2(vswp, "%s: adding multicast", __func__); 14291ae08745Sheppo if (vsw_add_mcst(vswp, VSW_LOCALDEV, addr, NULL) == 0) { 14301ae08745Sheppo /* 14311ae08745Sheppo * Update the list of multicast addresses 14321ae08745Sheppo * contained within the vsw_t structure to 14331ae08745Sheppo * include this new one. 14341ae08745Sheppo */ 14351ae08745Sheppo mcst_p = kmem_zalloc(sizeof (mcst_addr_t), KM_NOSLEEP); 14361ae08745Sheppo if (mcst_p == NULL) { 14371ae08745Sheppo DERR(vswp, "%s unable to alloc mem", __func__); 14381ae08745Sheppo return (1); 14391ae08745Sheppo } 14401ae08745Sheppo mcst_p->addr = addr; 14411ae08745Sheppo 14421ae08745Sheppo mutex_enter(&vswp->mca_lock); 14431ae08745Sheppo mcst_p->nextp = vswp->mcap; 14441ae08745Sheppo vswp->mcap = mcst_p; 14451ae08745Sheppo mutex_exit(&vswp->mca_lock); 14461ae08745Sheppo 14471ae08745Sheppo /* 14481ae08745Sheppo * Call into the underlying driver to program the 14491ae08745Sheppo * address into HW. 14501ae08745Sheppo * 14511ae08745Sheppo * Note: 14521ae08745Sheppo * Can safely ignore the return value as the card 14531ae08745Sheppo * will for the moment always be in promisc mode. 14541ae08745Sheppo * When we can program multiple MAC addresses into the 14551ae08745Sheppo * HW then we will need to care about the return 14561ae08745Sheppo * value here. 14571ae08745Sheppo */ 14581ae08745Sheppo if (vswp->mh != NULL) 14591ae08745Sheppo (void) mac_multicst_add(vswp->mh, mca); 14601ae08745Sheppo } 14611ae08745Sheppo } else { 14621ae08745Sheppo D2(vswp, "%s: removing multicast", __func__); 14631ae08745Sheppo /* 14641ae08745Sheppo * Remove the address from the hash table.. 14651ae08745Sheppo */ 14661ae08745Sheppo if (vsw_del_mcst(vswp, VSW_LOCALDEV, addr, NULL) == 0) { 14671ae08745Sheppo 14681ae08745Sheppo /* 14691ae08745Sheppo * ..and then from the list maintained in the 14701ae08745Sheppo * vsw_t structure. 14711ae08745Sheppo */ 14721ae08745Sheppo vsw_del_addr(VSW_LOCALDEV, vswp, addr); 14731ae08745Sheppo 14741ae08745Sheppo if (vswp->mh != NULL) 14751ae08745Sheppo (void) mac_multicst_remove(vswp->mh, mca); 14761ae08745Sheppo } 14771ae08745Sheppo } 14781ae08745Sheppo 14791ae08745Sheppo D1(vswp, "%s: exit", __func__); 14801ae08745Sheppo 14811ae08745Sheppo return (0); 14821ae08745Sheppo } 14831ae08745Sheppo 14841ae08745Sheppo static int 14851ae08745Sheppo vsw_m_promisc(void *arg, boolean_t on) 14861ae08745Sheppo { 14871ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 14881ae08745Sheppo 14891ae08745Sheppo D1(vswp, "%s: enter", __func__); 14901ae08745Sheppo 14911ae08745Sheppo WRITE_ENTER(&vswp->if_lockrw); 14921ae08745Sheppo if (on) 14931ae08745Sheppo vswp->if_state |= VSW_IF_PROMISC; 14941ae08745Sheppo else 14951ae08745Sheppo vswp->if_state &= ~VSW_IF_PROMISC; 14961ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 14971ae08745Sheppo 14981ae08745Sheppo D1(vswp, "%s: exit", __func__); 14991ae08745Sheppo 15001ae08745Sheppo return (0); 15011ae08745Sheppo } 15021ae08745Sheppo 15031ae08745Sheppo static mblk_t * 15041ae08745Sheppo vsw_m_tx(void *arg, mblk_t *mp) 15051ae08745Sheppo { 15061ae08745Sheppo vsw_t *vswp = (vsw_t *)arg; 15071ae08745Sheppo 15081ae08745Sheppo D1(vswp, "%s: enter", __func__); 15091ae08745Sheppo 15101ae08745Sheppo vsw_switch_frame(vswp, mp, VSW_LOCALDEV, NULL, NULL); 15111ae08745Sheppo 15121ae08745Sheppo D1(vswp, "%s: exit", __func__); 15131ae08745Sheppo 15141ae08745Sheppo return (NULL); 15151ae08745Sheppo } 15161ae08745Sheppo 15171ae08745Sheppo /* 15181ae08745Sheppo * Register for machine description (MD) updates. 15191ae08745Sheppo */ 15201ae08745Sheppo static void 15211ae08745Sheppo vsw_mdeg_register(vsw_t *vswp) 15221ae08745Sheppo { 15231ae08745Sheppo mdeg_prop_spec_t *pspecp; 15241ae08745Sheppo mdeg_node_spec_t *inst_specp; 15251ae08745Sheppo mdeg_handle_t mdeg_hdl; 15261ae08745Sheppo size_t templatesz; 15271ae08745Sheppo int inst, rv; 15281ae08745Sheppo 15291ae08745Sheppo D1(vswp, "%s: enter", __func__); 15301ae08745Sheppo 15311ae08745Sheppo inst = ddi_prop_get_int(DDI_DEV_T_ANY, vswp->dip, 15321ae08745Sheppo DDI_PROP_DONTPASS, reg_propname, -1); 15331ae08745Sheppo if (inst == -1) { 15341ae08745Sheppo DERR(vswp, "%s: unable to get %s property", 15351ae08745Sheppo __func__, reg_propname); 15361ae08745Sheppo return; 15371ae08745Sheppo } 15381ae08745Sheppo 15391ae08745Sheppo D2(vswp, "%s: instance %d registering with mdeg", __func__, inst); 15401ae08745Sheppo 15411ae08745Sheppo /* 15421ae08745Sheppo * Allocate and initialize a per-instance copy 15431ae08745Sheppo * of the global property spec array that will 15441ae08745Sheppo * uniquely identify this vsw instance. 15451ae08745Sheppo */ 15461ae08745Sheppo templatesz = sizeof (vsw_prop_template); 15471ae08745Sheppo pspecp = kmem_zalloc(templatesz, KM_SLEEP); 15481ae08745Sheppo 15491ae08745Sheppo bcopy(vsw_prop_template, pspecp, templatesz); 15501ae08745Sheppo 15511ae08745Sheppo VSW_SET_MDEG_PROP_INST(pspecp, inst); 15521ae08745Sheppo 15531ae08745Sheppo /* initialize the complete prop spec structure */ 15541ae08745Sheppo inst_specp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_SLEEP); 15551ae08745Sheppo inst_specp->namep = "virtual-device"; 15561ae08745Sheppo inst_specp->specp = pspecp; 15571ae08745Sheppo 15581ae08745Sheppo /* perform the registration */ 15591ae08745Sheppo rv = mdeg_register(inst_specp, &vport_match, vsw_mdeg_cb, 15601ae08745Sheppo (void *)vswp, &mdeg_hdl); 15611ae08745Sheppo 15621ae08745Sheppo if (rv != MDEG_SUCCESS) { 15631ae08745Sheppo DERR(vswp, "%s: mdeg_register failed (%d)\n", __func__, rv); 15641ae08745Sheppo kmem_free(inst_specp, sizeof (mdeg_node_spec_t)); 15651ae08745Sheppo kmem_free(pspecp, templatesz); 15661ae08745Sheppo return; 15671ae08745Sheppo } 15681ae08745Sheppo 15691ae08745Sheppo /* save off data that will be needed later */ 15701ae08745Sheppo vswp->inst_spec = inst_specp; 15711ae08745Sheppo vswp->mdeg_hdl = mdeg_hdl; 15721ae08745Sheppo 15731ae08745Sheppo D1(vswp, "%s: exit", __func__); 15741ae08745Sheppo } 15751ae08745Sheppo 15761ae08745Sheppo static void 15771ae08745Sheppo vsw_mdeg_unregister(vsw_t *vswp) 15781ae08745Sheppo { 15791ae08745Sheppo D1(vswp, "vsw_mdeg_unregister: enter"); 15801ae08745Sheppo 15811ae08745Sheppo (void) mdeg_unregister(vswp->mdeg_hdl); 15821ae08745Sheppo 15831ae08745Sheppo if (vswp->inst_spec->specp != NULL) { 15841ae08745Sheppo (void) kmem_free(vswp->inst_spec->specp, 15851ae08745Sheppo sizeof (vsw_prop_template)); 15861ae08745Sheppo vswp->inst_spec->specp = NULL; 15871ae08745Sheppo } 15881ae08745Sheppo 15891ae08745Sheppo if (vswp->inst_spec != NULL) { 15901ae08745Sheppo (void) kmem_free(vswp->inst_spec, 15911ae08745Sheppo sizeof (mdeg_node_spec_t)); 15921ae08745Sheppo vswp->inst_spec = NULL; 15931ae08745Sheppo } 15941ae08745Sheppo 15951ae08745Sheppo D1(vswp, "vsw_mdeg_unregister: exit"); 15961ae08745Sheppo } 15971ae08745Sheppo 15981ae08745Sheppo static int 15991ae08745Sheppo vsw_mdeg_cb(void *cb_argp, mdeg_result_t *resp) 16001ae08745Sheppo { 16011ae08745Sheppo vsw_t *vswp; 16021ae08745Sheppo int idx; 16031ae08745Sheppo md_t *mdp; 16041ae08745Sheppo mde_cookie_t node; 16051ae08745Sheppo uint64_t inst; 16061ae08745Sheppo 16071ae08745Sheppo if (resp == NULL) 16081ae08745Sheppo return (MDEG_FAILURE); 16091ae08745Sheppo 16101ae08745Sheppo vswp = (vsw_t *)cb_argp; 16111ae08745Sheppo 16121ae08745Sheppo D1(vswp, "%s: added %d : removed %d : matched %d", 16131ae08745Sheppo __func__, resp->added.nelem, resp->removed.nelem, 16141ae08745Sheppo resp->match_prev.nelem); 16151ae08745Sheppo 16161ae08745Sheppo /* process added ports */ 16171ae08745Sheppo for (idx = 0; idx < resp->added.nelem; idx++) { 16181ae08745Sheppo mdp = resp->added.mdp; 16191ae08745Sheppo node = resp->added.mdep[idx]; 16201ae08745Sheppo 16211ae08745Sheppo D2(vswp, "%s: adding node(%d) 0x%lx", __func__, idx, node); 16221ae08745Sheppo 16231ae08745Sheppo if (vsw_port_add(vswp, mdp, &node) != 0) { 16241ae08745Sheppo cmn_err(CE_WARN, "Unable to add new port (0x%lx)", 16251ae08745Sheppo node); 16261ae08745Sheppo } 16271ae08745Sheppo } 16281ae08745Sheppo 16291ae08745Sheppo /* process removed ports */ 16301ae08745Sheppo for (idx = 0; idx < resp->removed.nelem; idx++) { 16311ae08745Sheppo mdp = resp->removed.mdp; 16321ae08745Sheppo node = resp->removed.mdep[idx]; 16331ae08745Sheppo 16341ae08745Sheppo if (md_get_prop_val(mdp, node, id_propname, &inst)) { 16351ae08745Sheppo DERR(vswp, "%s: prop(%s) not found port(%d)", 16361ae08745Sheppo __func__, id_propname, idx); 16371ae08745Sheppo continue; 16381ae08745Sheppo } 16391ae08745Sheppo 16401ae08745Sheppo D2(vswp, "%s: removing node(%d) 0x%lx", __func__, idx, node); 16411ae08745Sheppo 16421ae08745Sheppo if (vsw_port_detach(vswp, inst) != 0) { 16431ae08745Sheppo cmn_err(CE_WARN, "Unable to remove port %ld", inst); 16441ae08745Sheppo } 16451ae08745Sheppo } 16461ae08745Sheppo 16471ae08745Sheppo /* 16481ae08745Sheppo * Currently no support for updating already active ports. 16491ae08745Sheppo * So, ignore the match_curr and match_priv arrays for now. 16501ae08745Sheppo */ 16511ae08745Sheppo 16521ae08745Sheppo D1(vswp, "%s: exit", __func__); 16531ae08745Sheppo 16541ae08745Sheppo return (MDEG_SUCCESS); 16551ae08745Sheppo } 16561ae08745Sheppo 16571ae08745Sheppo /* 16581ae08745Sheppo * Add a new port to the system. 16591ae08745Sheppo * 16601ae08745Sheppo * Returns 0 on success, 1 on failure. 16611ae08745Sheppo */ 16621ae08745Sheppo int 16631ae08745Sheppo vsw_port_add(vsw_t *vswp, md_t *mdp, mde_cookie_t *node) 16641ae08745Sheppo { 16651ae08745Sheppo uint64_t ldc_id; 16661ae08745Sheppo uint8_t *addrp; 16671ae08745Sheppo int i, addrsz; 16681ae08745Sheppo int num_nodes = 0, nchan = 0; 16691ae08745Sheppo int listsz = 0; 16701ae08745Sheppo mde_cookie_t *listp = NULL; 16711ae08745Sheppo struct ether_addr ea; 16721ae08745Sheppo uint64_t macaddr; 16731ae08745Sheppo uint64_t inst = 0; 16741ae08745Sheppo vsw_port_t *port; 16751ae08745Sheppo 16761ae08745Sheppo if (md_get_prop_val(mdp, *node, id_propname, &inst)) { 16771ae08745Sheppo DWARN(vswp, "%s: prop(%s) not found", __func__, 16781ae08745Sheppo id_propname); 16791ae08745Sheppo return (1); 16801ae08745Sheppo } 16811ae08745Sheppo 16821ae08745Sheppo /* 16831ae08745Sheppo * Find the channel endpoint node(s) (which should be under this 16841ae08745Sheppo * port node) which contain the channel id(s). 16851ae08745Sheppo */ 16861ae08745Sheppo if ((num_nodes = md_node_count(mdp)) <= 0) { 16871ae08745Sheppo DERR(vswp, "%s: invalid number of nodes found (%d)", 16881ae08745Sheppo __func__, num_nodes); 16891ae08745Sheppo return (1); 16901ae08745Sheppo } 16911ae08745Sheppo 16921ae08745Sheppo /* allocate enough space for node list */ 16931ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t); 16941ae08745Sheppo listp = kmem_zalloc(listsz, KM_SLEEP); 16951ae08745Sheppo 16961ae08745Sheppo nchan = md_scan_dag(mdp, *node, 16971ae08745Sheppo md_find_name(mdp, chan_propname), 16981ae08745Sheppo md_find_name(mdp, "fwd"), listp); 16991ae08745Sheppo 17001ae08745Sheppo if (nchan <= 0) { 17011ae08745Sheppo DWARN(vswp, "%s: no %s nodes found", __func__, chan_propname); 17021ae08745Sheppo kmem_free(listp, listsz); 17031ae08745Sheppo return (1); 17041ae08745Sheppo } 17051ae08745Sheppo 17061ae08745Sheppo D2(vswp, "%s: %d %s nodes found", __func__, nchan, chan_propname); 17071ae08745Sheppo 17081ae08745Sheppo /* use property from first node found */ 17091ae08745Sheppo if (md_get_prop_val(mdp, listp[0], id_propname, &ldc_id)) { 17101ae08745Sheppo DWARN(vswp, "%s: prop(%s) not found\n", __func__, 17111ae08745Sheppo id_propname); 17121ae08745Sheppo kmem_free(listp, listsz); 17131ae08745Sheppo return (1); 17141ae08745Sheppo } 17151ae08745Sheppo 17161ae08745Sheppo /* don't need list any more */ 17171ae08745Sheppo kmem_free(listp, listsz); 17181ae08745Sheppo 17191ae08745Sheppo D2(vswp, "%s: ldc_id 0x%llx", __func__, ldc_id); 17201ae08745Sheppo 17211ae08745Sheppo /* read mac-address property */ 17221ae08745Sheppo if (md_get_prop_data(mdp, *node, remaddr_propname, 17231ae08745Sheppo &addrp, &addrsz)) { 17241ae08745Sheppo DWARN(vswp, "%s: prop(%s) not found", 17251ae08745Sheppo __func__, remaddr_propname); 17261ae08745Sheppo return (1); 17271ae08745Sheppo } 17281ae08745Sheppo 17291ae08745Sheppo if (addrsz < ETHERADDRL) { 17301ae08745Sheppo DWARN(vswp, "%s: invalid address size", __func__); 17311ae08745Sheppo return (1); 17321ae08745Sheppo } 17331ae08745Sheppo 17341ae08745Sheppo macaddr = *((uint64_t *)addrp); 17351ae08745Sheppo D2(vswp, "%s: remote mac address 0x%llx", __func__, macaddr); 17361ae08745Sheppo 17371ae08745Sheppo for (i = ETHERADDRL - 1; i >= 0; i--) { 17381ae08745Sheppo ea.ether_addr_octet[i] = macaddr & 0xFF; 17391ae08745Sheppo macaddr >>= 8; 17401ae08745Sheppo } 17411ae08745Sheppo 17421ae08745Sheppo if (vsw_port_attach(vswp, (int)inst, &ldc_id, 1, &ea) != 0) { 17431ae08745Sheppo DERR(vswp, "%s: failed to attach port", __func__); 17441ae08745Sheppo return (1); 17451ae08745Sheppo } 17461ae08745Sheppo 17471ae08745Sheppo port = vsw_lookup_port(vswp, (int)inst); 17481ae08745Sheppo 17491ae08745Sheppo /* just successfuly created the port, so it should exist */ 17501ae08745Sheppo ASSERT(port != NULL); 17511ae08745Sheppo 17521ae08745Sheppo return (0); 17531ae08745Sheppo } 17541ae08745Sheppo 17551ae08745Sheppo /* 17561ae08745Sheppo * Attach the specified port. 17571ae08745Sheppo * 17581ae08745Sheppo * Returns 0 on success, 1 on failure. 17591ae08745Sheppo */ 17601ae08745Sheppo static int 17611ae08745Sheppo vsw_port_attach(vsw_t *vswp, int p_instance, uint64_t *ldcids, int nids, 17621ae08745Sheppo struct ether_addr *macaddr) 17631ae08745Sheppo { 17641ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 17651ae08745Sheppo vsw_port_t *port, **prev_port; 17661ae08745Sheppo int i; 17671ae08745Sheppo 17681ae08745Sheppo D1(vswp, "%s: enter : port %d", __func__, p_instance); 17691ae08745Sheppo 17701ae08745Sheppo /* port already exists? */ 17711ae08745Sheppo READ_ENTER(&plist->lockrw); 17721ae08745Sheppo for (port = plist->head; port != NULL; port = port->p_next) { 17731ae08745Sheppo if (port->p_instance == p_instance) { 17741ae08745Sheppo DWARN(vswp, "%s: port instance %d already attached", 17751ae08745Sheppo __func__, p_instance); 17761ae08745Sheppo RW_EXIT(&plist->lockrw); 17771ae08745Sheppo return (1); 17781ae08745Sheppo } 17791ae08745Sheppo } 17801ae08745Sheppo RW_EXIT(&plist->lockrw); 17811ae08745Sheppo 17821ae08745Sheppo port = kmem_zalloc(sizeof (vsw_port_t), KM_SLEEP); 17831ae08745Sheppo port->p_vswp = vswp; 17841ae08745Sheppo port->p_instance = p_instance; 17851ae08745Sheppo port->p_ldclist.num_ldcs = 0; 17861ae08745Sheppo port->p_ldclist.head = NULL; 17871ae08745Sheppo 17881ae08745Sheppo rw_init(&port->p_ldclist.lockrw, NULL, RW_DRIVER, NULL); 17891ae08745Sheppo 17901ae08745Sheppo mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL); 17911ae08745Sheppo mutex_init(&port->mca_lock, NULL, MUTEX_DRIVER, NULL); 17921ae08745Sheppo 17931ae08745Sheppo mutex_init(&port->ref_lock, NULL, MUTEX_DRIVER, NULL); 17941ae08745Sheppo cv_init(&port->ref_cv, NULL, CV_DRIVER, NULL); 17951ae08745Sheppo 17961ae08745Sheppo mutex_init(&port->state_lock, NULL, MUTEX_DRIVER, NULL); 17971ae08745Sheppo cv_init(&port->state_cv, NULL, CV_DRIVER, NULL); 17981ae08745Sheppo port->state = VSW_PORT_INIT; 17991ae08745Sheppo 18001ae08745Sheppo if (nids > VSW_PORT_MAX_LDCS) { 18011ae08745Sheppo D2(vswp, "%s: using first of %d ldc ids", 18021ae08745Sheppo __func__, nids); 18031ae08745Sheppo nids = VSW_PORT_MAX_LDCS; 18041ae08745Sheppo } 18051ae08745Sheppo 18061ae08745Sheppo D2(vswp, "%s: %d nids", __func__, nids); 18071ae08745Sheppo for (i = 0; i < nids; i++) { 18081ae08745Sheppo D2(vswp, "%s: ldcid (%llx)", __func__, (uint64_t)ldcids[i]); 18091ae08745Sheppo if (vsw_ldc_attach(port, (uint64_t)ldcids[i]) != 0) { 18101ae08745Sheppo DERR(vswp, "%s: ldc_attach failed", __func__); 18111ae08745Sheppo 18121ae08745Sheppo rw_destroy(&port->p_ldclist.lockrw); 18131ae08745Sheppo 18141ae08745Sheppo cv_destroy(&port->ref_cv); 18151ae08745Sheppo mutex_destroy(&port->ref_lock); 18161ae08745Sheppo 18171ae08745Sheppo cv_destroy(&port->state_cv); 18181ae08745Sheppo mutex_destroy(&port->state_lock); 18191ae08745Sheppo 18201ae08745Sheppo mutex_destroy(&port->tx_lock); 18211ae08745Sheppo mutex_destroy(&port->mca_lock); 18221ae08745Sheppo kmem_free(port, sizeof (vsw_port_t)); 18231ae08745Sheppo return (1); 18241ae08745Sheppo } 18251ae08745Sheppo } 18261ae08745Sheppo 18271ae08745Sheppo ether_copy(macaddr, &port->p_macaddr); 18281ae08745Sheppo 18291ae08745Sheppo WRITE_ENTER(&plist->lockrw); 18301ae08745Sheppo 18311ae08745Sheppo /* create the fdb entry for this port/mac address */ 18321ae08745Sheppo (void) vsw_add_fdb(vswp, port); 18331ae08745Sheppo 18341ae08745Sheppo /* link it into the list of ports for this vsw instance */ 18351ae08745Sheppo prev_port = (vsw_port_t **)(&plist->head); 18361ae08745Sheppo port->p_next = *prev_port; 18371ae08745Sheppo *prev_port = port; 18381ae08745Sheppo plist->num_ports++; 18391ae08745Sheppo RW_EXIT(&plist->lockrw); 18401ae08745Sheppo 18411ae08745Sheppo /* 18421ae08745Sheppo * Initialise the port and any ldc's under it. 18431ae08745Sheppo */ 18441ae08745Sheppo (void) vsw_init_ldcs(port); 18451ae08745Sheppo 18461ae08745Sheppo D1(vswp, "%s: exit", __func__); 18471ae08745Sheppo return (0); 18481ae08745Sheppo } 18491ae08745Sheppo 18501ae08745Sheppo /* 18511ae08745Sheppo * Detach the specified port. 18521ae08745Sheppo * 18531ae08745Sheppo * Returns 0 on success, 1 on failure. 18541ae08745Sheppo */ 18551ae08745Sheppo static int 18561ae08745Sheppo vsw_port_detach(vsw_t *vswp, int p_instance) 18571ae08745Sheppo { 18581ae08745Sheppo vsw_port_t *port = NULL; 18591ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 18601ae08745Sheppo 18611ae08745Sheppo D1(vswp, "%s: enter: port id %d", __func__, p_instance); 18621ae08745Sheppo 18631ae08745Sheppo WRITE_ENTER(&plist->lockrw); 18641ae08745Sheppo 18651ae08745Sheppo if ((port = vsw_lookup_port(vswp, p_instance)) == NULL) { 18661ae08745Sheppo RW_EXIT(&plist->lockrw); 18671ae08745Sheppo return (1); 18681ae08745Sheppo } 18691ae08745Sheppo 18701ae08745Sheppo if (vsw_plist_del_node(vswp, port)) { 18711ae08745Sheppo RW_EXIT(&plist->lockrw); 18721ae08745Sheppo return (1); 18731ae08745Sheppo } 18741ae08745Sheppo 18751ae08745Sheppo /* Remove the fdb entry for this port/mac address */ 18761ae08745Sheppo (void) vsw_del_fdb(vswp, port); 18771ae08745Sheppo 18781ae08745Sheppo /* Remove any multicast addresses.. */ 18791ae08745Sheppo vsw_del_mcst_port(port); 18801ae08745Sheppo 18811ae08745Sheppo /* 18821ae08745Sheppo * No longer need to hold lock on port list now that we 18831ae08745Sheppo * have unlinked the target port from the list. 18841ae08745Sheppo */ 18851ae08745Sheppo RW_EXIT(&plist->lockrw); 18861ae08745Sheppo 18871ae08745Sheppo if (vsw_port_delete(port)) { 18881ae08745Sheppo return (1); 18891ae08745Sheppo } 18901ae08745Sheppo 18911ae08745Sheppo D1(vswp, "%s: exit: p_instance(%d)", __func__, p_instance); 18921ae08745Sheppo return (0); 18931ae08745Sheppo } 18941ae08745Sheppo 18951ae08745Sheppo /* 18961ae08745Sheppo * Detach all active ports. 18971ae08745Sheppo * 18981ae08745Sheppo * Returns 0 on success, 1 on failure. 18991ae08745Sheppo */ 19001ae08745Sheppo static int 19011ae08745Sheppo vsw_detach_ports(vsw_t *vswp) 19021ae08745Sheppo { 19031ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 19041ae08745Sheppo vsw_port_t *port = NULL; 19051ae08745Sheppo 19061ae08745Sheppo D1(vswp, "%s: enter", __func__); 19071ae08745Sheppo 19081ae08745Sheppo WRITE_ENTER(&plist->lockrw); 19091ae08745Sheppo 19101ae08745Sheppo while ((port = plist->head) != NULL) { 19111ae08745Sheppo if (vsw_plist_del_node(vswp, port)) { 19121ae08745Sheppo DERR(vswp, "%s: Error deleting port %d" 19131ae08745Sheppo " from port list", __func__, 19141ae08745Sheppo port->p_instance); 19151ae08745Sheppo RW_EXIT(&plist->lockrw); 19161ae08745Sheppo return (1); 19171ae08745Sheppo } 19181ae08745Sheppo 19191ae08745Sheppo /* Remove the fdb entry for this port/mac address */ 19201ae08745Sheppo (void) vsw_del_fdb(vswp, port); 19211ae08745Sheppo 19221ae08745Sheppo /* Remove any multicast addresses.. */ 19231ae08745Sheppo vsw_del_mcst_port(port); 19241ae08745Sheppo 19251ae08745Sheppo /* 19261ae08745Sheppo * No longer need to hold the lock on the port list 19271ae08745Sheppo * now that we have unlinked the target port from the 19281ae08745Sheppo * list. 19291ae08745Sheppo */ 19301ae08745Sheppo RW_EXIT(&plist->lockrw); 19311ae08745Sheppo if (vsw_port_delete(port)) { 19321ae08745Sheppo DERR(vswp, "%s: Error deleting port %d", 19331ae08745Sheppo __func__, port->p_instance); 19341ae08745Sheppo return (1); 19351ae08745Sheppo } 19361ae08745Sheppo WRITE_ENTER(&plist->lockrw); 19371ae08745Sheppo } 19381ae08745Sheppo RW_EXIT(&plist->lockrw); 19391ae08745Sheppo 19401ae08745Sheppo D1(vswp, "%s: exit", __func__); 19411ae08745Sheppo 19421ae08745Sheppo return (0); 19431ae08745Sheppo } 19441ae08745Sheppo 19451ae08745Sheppo /* 19461ae08745Sheppo * Delete the specified port. 19471ae08745Sheppo * 19481ae08745Sheppo * Returns 0 on success, 1 on failure. 19491ae08745Sheppo */ 19501ae08745Sheppo static int 19511ae08745Sheppo vsw_port_delete(vsw_port_t *port) 19521ae08745Sheppo { 19531ae08745Sheppo vsw_ldc_list_t *ldcl; 19541ae08745Sheppo vsw_t *vswp = port->p_vswp; 19551ae08745Sheppo 19561ae08745Sheppo D1(vswp, "%s: enter : port id %d", __func__, port->p_instance); 19571ae08745Sheppo 19581ae08745Sheppo (void) vsw_uninit_ldcs(port); 19591ae08745Sheppo 19601ae08745Sheppo /* 19611ae08745Sheppo * Wait for any pending ctrl msg tasks which reference this 19621ae08745Sheppo * port to finish. 19631ae08745Sheppo */ 19641ae08745Sheppo if (vsw_drain_port_taskq(port)) 19651ae08745Sheppo return (1); 19661ae08745Sheppo 19671ae08745Sheppo /* 19681ae08745Sheppo * Wait for port reference count to hit zero. 19691ae08745Sheppo */ 19701ae08745Sheppo mutex_enter(&port->ref_lock); 19711ae08745Sheppo while (port->ref_cnt != 0) 19721ae08745Sheppo cv_wait(&port->ref_cv, &port->ref_lock); 19731ae08745Sheppo mutex_exit(&port->ref_lock); 19741ae08745Sheppo 19751ae08745Sheppo /* 19761ae08745Sheppo * Wait for any active callbacks to finish 19771ae08745Sheppo */ 19781ae08745Sheppo if (vsw_drain_ldcs(port)) 19791ae08745Sheppo return (1); 19801ae08745Sheppo 19811ae08745Sheppo ldcl = &port->p_ldclist; 19821ae08745Sheppo WRITE_ENTER(&ldcl->lockrw); 19831ae08745Sheppo while (ldcl->num_ldcs > 0) { 19841ae08745Sheppo if (vsw_ldc_detach(port, ldcl->head->ldc_id) != 0) {; 19851ae08745Sheppo cmn_err(CE_WARN, "unable to detach ldc %ld", 19861ae08745Sheppo ldcl->head->ldc_id); 19871ae08745Sheppo RW_EXIT(&ldcl->lockrw); 19881ae08745Sheppo return (1); 19891ae08745Sheppo } 19901ae08745Sheppo } 19911ae08745Sheppo RW_EXIT(&ldcl->lockrw); 19921ae08745Sheppo 19931ae08745Sheppo rw_destroy(&port->p_ldclist.lockrw); 19941ae08745Sheppo 19951ae08745Sheppo mutex_destroy(&port->mca_lock); 19961ae08745Sheppo mutex_destroy(&port->tx_lock); 19971ae08745Sheppo cv_destroy(&port->ref_cv); 19981ae08745Sheppo mutex_destroy(&port->ref_lock); 19991ae08745Sheppo 20001ae08745Sheppo cv_destroy(&port->state_cv); 20011ae08745Sheppo mutex_destroy(&port->state_lock); 20021ae08745Sheppo 20031ae08745Sheppo kmem_free(port, sizeof (vsw_port_t)); 20041ae08745Sheppo 20051ae08745Sheppo D1(vswp, "%s: exit", __func__); 20061ae08745Sheppo 20071ae08745Sheppo return (0); 20081ae08745Sheppo } 20091ae08745Sheppo 20101ae08745Sheppo /* 20111ae08745Sheppo * Attach a logical domain channel (ldc) under a specified port. 20121ae08745Sheppo * 20131ae08745Sheppo * Returns 0 on success, 1 on failure. 20141ae08745Sheppo */ 20151ae08745Sheppo static int 20161ae08745Sheppo vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id) 20171ae08745Sheppo { 20181ae08745Sheppo vsw_t *vswp = port->p_vswp; 20191ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 20201ae08745Sheppo vsw_ldc_t *ldcp = NULL; 20211ae08745Sheppo ldc_attr_t attr; 20221ae08745Sheppo ldc_status_t istatus; 20231ae08745Sheppo int status = DDI_FAILURE; 20241ae08745Sheppo 20251ae08745Sheppo D1(vswp, "%s: enter", __func__); 20261ae08745Sheppo 20271ae08745Sheppo ldcp = kmem_zalloc(sizeof (vsw_ldc_t), KM_NOSLEEP); 20281ae08745Sheppo if (ldcp == NULL) { 20291ae08745Sheppo DERR(vswp, "%s: kmem_zalloc failed", __func__); 20301ae08745Sheppo return (1); 20311ae08745Sheppo } 20321ae08745Sheppo ldcp->ldc_id = ldc_id; 20331ae08745Sheppo 20341ae08745Sheppo mutex_init(&ldcp->ldc_txlock, NULL, MUTEX_DRIVER, NULL); 20351ae08745Sheppo mutex_init(&ldcp->ldc_cblock, NULL, MUTEX_DRIVER, NULL); 20361ae08745Sheppo mutex_init(&ldcp->drain_cv_lock, NULL, MUTEX_DRIVER, NULL); 20371ae08745Sheppo cv_init(&ldcp->drain_cv, NULL, CV_DRIVER, NULL); 20381ae08745Sheppo 20391ae08745Sheppo /* required for handshake with peer */ 20401ae08745Sheppo ldcp->local_session = (uint64_t)ddi_get_lbolt(); 20411ae08745Sheppo ldcp->peer_session = 0; 20421ae08745Sheppo ldcp->session_status = 0; 20431ae08745Sheppo 20441ae08745Sheppo mutex_init(&ldcp->hss_lock, NULL, MUTEX_DRIVER, NULL); 20451ae08745Sheppo ldcp->hss_id = 1; /* Initial handshake session id */ 20461ae08745Sheppo 20471ae08745Sheppo /* only set for outbound lane, inbound set by peer */ 20481ae08745Sheppo vsw_set_lane_attr(vswp, &ldcp->lane_out); 20491ae08745Sheppo 20501ae08745Sheppo attr.devclass = LDC_DEV_NT_SVC; 20511ae08745Sheppo attr.instance = ddi_get_instance(vswp->dip); 20521ae08745Sheppo attr.mode = LDC_MODE_UNRELIABLE; 20531ae08745Sheppo attr.qlen = VSW_LDC_QLEN; 20541ae08745Sheppo status = ldc_init(ldc_id, &attr, &ldcp->ldc_handle); 20551ae08745Sheppo if (status != 0) { 20561ae08745Sheppo DERR(vswp, "%s(%lld): ldc_init failed, rv (%d)", 20571ae08745Sheppo __func__, ldc_id, status); 20581ae08745Sheppo mutex_destroy(&ldcp->ldc_txlock); 20591ae08745Sheppo mutex_destroy(&ldcp->ldc_cblock); 20601ae08745Sheppo cv_destroy(&ldcp->drain_cv); 20611ae08745Sheppo mutex_destroy(&ldcp->drain_cv_lock); 20621ae08745Sheppo mutex_destroy(&ldcp->hss_lock); 20631ae08745Sheppo kmem_free(ldcp, sizeof (vsw_ldc_t)); 20641ae08745Sheppo return (1); 20651ae08745Sheppo } 20661ae08745Sheppo 20671ae08745Sheppo status = ldc_reg_callback(ldcp->ldc_handle, vsw_ldc_cb, (caddr_t)ldcp); 20681ae08745Sheppo if (status != 0) { 20691ae08745Sheppo DERR(vswp, "%s(%lld): ldc_reg_callback failed, rv (%d)", 20701ae08745Sheppo __func__, ldc_id, status); 20711ae08745Sheppo mutex_destroy(&ldcp->ldc_txlock); 20721ae08745Sheppo mutex_destroy(&ldcp->ldc_cblock); 20731ae08745Sheppo cv_destroy(&ldcp->drain_cv); 20741ae08745Sheppo mutex_destroy(&ldcp->drain_cv_lock); 20751ae08745Sheppo mutex_destroy(&ldcp->hss_lock); 20761ae08745Sheppo (void) ldc_fini(ldcp->ldc_handle); 20771ae08745Sheppo kmem_free(ldcp, sizeof (vsw_ldc_t)); 20781ae08745Sheppo return (1); 20791ae08745Sheppo } 20801ae08745Sheppo 20811ae08745Sheppo 20821ae08745Sheppo if (ldc_status(ldcp->ldc_handle, &istatus) != 0) { 20831ae08745Sheppo DERR(vswp, "%s: ldc_status failed", __func__); 20841ae08745Sheppo return (1); 20851ae08745Sheppo } 20861ae08745Sheppo 20871ae08745Sheppo ldcp->ldc_status = istatus; 20881ae08745Sheppo ldcp->ldc_port = port; 20891ae08745Sheppo ldcp->ldc_vswp = vswp; 20901ae08745Sheppo 20911ae08745Sheppo /* link it into the list of channels for this port */ 20921ae08745Sheppo WRITE_ENTER(&ldcl->lockrw); 20931ae08745Sheppo ldcp->ldc_next = ldcl->head; 20941ae08745Sheppo ldcl->head = ldcp; 20951ae08745Sheppo ldcl->num_ldcs++; 20961ae08745Sheppo RW_EXIT(&ldcl->lockrw); 20971ae08745Sheppo 20981ae08745Sheppo D1(vswp, "%s: exit", __func__); 20991ae08745Sheppo return (0); 21001ae08745Sheppo } 21011ae08745Sheppo 21021ae08745Sheppo /* 21031ae08745Sheppo * Detach a logical domain channel (ldc) belonging to a 21041ae08745Sheppo * particular port. 21051ae08745Sheppo * 21061ae08745Sheppo * Returns 0 on success, 1 on failure. 21071ae08745Sheppo */ 21081ae08745Sheppo static int 21091ae08745Sheppo vsw_ldc_detach(vsw_port_t *port, uint64_t ldc_id) 21101ae08745Sheppo { 21111ae08745Sheppo vsw_t *vswp = port->p_vswp; 21121ae08745Sheppo vsw_ldc_t *ldcp, *prev_ldcp; 21131ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 21141ae08745Sheppo int rv; 21151ae08745Sheppo 21161ae08745Sheppo prev_ldcp = ldcl->head; 21171ae08745Sheppo for (; (ldcp = prev_ldcp) != NULL; prev_ldcp = ldcp->ldc_next) { 21181ae08745Sheppo if (ldcp->ldc_id == ldc_id) { 21191ae08745Sheppo break; 21201ae08745Sheppo } 21211ae08745Sheppo } 21221ae08745Sheppo 21231ae08745Sheppo /* specified ldc id not found */ 21241ae08745Sheppo if (ldcp == NULL) { 21251ae08745Sheppo DERR(vswp, "%s: ldcp = NULL", __func__); 21261ae08745Sheppo return (1); 21271ae08745Sheppo } 21281ae08745Sheppo 21291ae08745Sheppo D2(vswp, "%s: detaching channel %lld", __func__, ldcp->ldc_id); 21301ae08745Sheppo 21311ae08745Sheppo /* 21321ae08745Sheppo * Before we can close the channel we must release any mapped 21331ae08745Sheppo * resources (e.g. drings). 21341ae08745Sheppo */ 21351ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 21361ae08745Sheppo vsw_free_lane_resources(ldcp, OUTBOUND); 21371ae08745Sheppo 21381ae08745Sheppo /* 21391ae08745Sheppo * If the close fails we are in serious trouble, as won't 21401ae08745Sheppo * be able to delete the parent port. 21411ae08745Sheppo */ 21421ae08745Sheppo if ((rv = ldc_close(ldcp->ldc_handle)) != 0) { 21431ae08745Sheppo DERR(vswp, "%s: error %d closing channel %lld", 21441ae08745Sheppo __func__, rv, ldcp->ldc_id); 21451ae08745Sheppo return (1); 21461ae08745Sheppo } 21471ae08745Sheppo 21481ae08745Sheppo (void) ldc_fini(ldcp->ldc_handle); 21491ae08745Sheppo 21501ae08745Sheppo ldcp->ldc_status = LDC_INIT; 21511ae08745Sheppo ldcp->ldc_handle = NULL; 21521ae08745Sheppo ldcp->ldc_vswp = NULL; 21531ae08745Sheppo mutex_destroy(&ldcp->ldc_txlock); 21541ae08745Sheppo mutex_destroy(&ldcp->ldc_cblock); 21551ae08745Sheppo cv_destroy(&ldcp->drain_cv); 21561ae08745Sheppo mutex_destroy(&ldcp->drain_cv_lock); 21571ae08745Sheppo mutex_destroy(&ldcp->hss_lock); 21581ae08745Sheppo 21591ae08745Sheppo /* unlink it from the list */ 21601ae08745Sheppo prev_ldcp = ldcp->ldc_next; 21611ae08745Sheppo ldcl->num_ldcs--; 21621ae08745Sheppo kmem_free(ldcp, sizeof (vsw_ldc_t)); 21631ae08745Sheppo 21641ae08745Sheppo return (0); 21651ae08745Sheppo } 21661ae08745Sheppo 21671ae08745Sheppo /* 21681ae08745Sheppo * Open and attempt to bring up the channel. Note that channel 21691ae08745Sheppo * can only be brought up if peer has also opened channel. 21701ae08745Sheppo * 21711ae08745Sheppo * Returns 0 if can open and bring up channel, otherwise 21721ae08745Sheppo * returns 1. 21731ae08745Sheppo */ 21741ae08745Sheppo static int 21751ae08745Sheppo vsw_ldc_init(vsw_ldc_t *ldcp) 21761ae08745Sheppo { 21771ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 21781ae08745Sheppo ldc_status_t istatus = 0; 21791ae08745Sheppo int rv; 21801ae08745Sheppo 21811ae08745Sheppo D1(vswp, "%s: enter", __func__); 21821ae08745Sheppo 21831ae08745Sheppo LDC_ENTER_LOCK(ldcp); 21841ae08745Sheppo 21851ae08745Sheppo /* don't start at 0 in case clients don't like that */ 21861ae08745Sheppo ldcp->next_ident = 1; 21871ae08745Sheppo 21881ae08745Sheppo rv = ldc_open(ldcp->ldc_handle); 21891ae08745Sheppo if (rv != 0) { 21901ae08745Sheppo DERR(vswp, "%s: ldc_open failed: id(%lld) rv(%d)", 21911ae08745Sheppo __func__, ldcp->ldc_id, rv); 21921ae08745Sheppo LDC_EXIT_LOCK(ldcp); 21931ae08745Sheppo return (1); 21941ae08745Sheppo } 21951ae08745Sheppo 21961ae08745Sheppo if (ldc_status(ldcp->ldc_handle, &istatus) != 0) { 21971ae08745Sheppo DERR(vswp, "%s: unable to get status", __func__); 21981ae08745Sheppo LDC_EXIT_LOCK(ldcp); 21991ae08745Sheppo return (1); 22001ae08745Sheppo 22011ae08745Sheppo } else if (istatus != LDC_OPEN && istatus != LDC_READY) { 22021ae08745Sheppo DERR(vswp, "%s: id (%lld) status(%d) is not OPEN/READY", 22031ae08745Sheppo __func__, ldcp->ldc_id, istatus); 22041ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22051ae08745Sheppo return (1); 22061ae08745Sheppo } 22071ae08745Sheppo 22081ae08745Sheppo ldcp->ldc_status = istatus; 22091ae08745Sheppo rv = ldc_up(ldcp->ldc_handle); 22101ae08745Sheppo if (rv != 0) { 22111ae08745Sheppo /* 22121ae08745Sheppo * Not a fatal error for ldc_up() to fail, as peer 22131ae08745Sheppo * end point may simply not be ready yet. 22141ae08745Sheppo */ 22151ae08745Sheppo D2(vswp, "%s: ldc_up err id(%lld) rv(%d)", __func__, 22161ae08745Sheppo ldcp->ldc_id, rv); 22171ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22181ae08745Sheppo return (1); 22191ae08745Sheppo } 22201ae08745Sheppo 22211ae08745Sheppo /* 22221ae08745Sheppo * ldc_up() call is non-blocking so need to explicitly 22231ae08745Sheppo * check channel status to see if in fact the channel 22241ae08745Sheppo * is UP. 22251ae08745Sheppo */ 22261ae08745Sheppo if (ldc_status(ldcp->ldc_handle, &istatus) != 0) { 22271ae08745Sheppo DERR(vswp, "%s: unable to get status", __func__); 22281ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22291ae08745Sheppo return (1); 22301ae08745Sheppo 22311ae08745Sheppo } else if (istatus != LDC_UP) { 22321ae08745Sheppo DERR(vswp, "%s: id(%lld) status(%d) is not UP", 22331ae08745Sheppo __func__, ldcp->ldc_id, istatus); 22341ae08745Sheppo } else { 22351ae08745Sheppo ldcp->ldc_status = istatus; 22361ae08745Sheppo } 22371ae08745Sheppo 22381ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22391ae08745Sheppo 22401ae08745Sheppo D1(vswp, "%s: exit", __func__); 22411ae08745Sheppo return (0); 22421ae08745Sheppo } 22431ae08745Sheppo 22441ae08745Sheppo /* disable callbacks on the channel */ 22451ae08745Sheppo static int 22461ae08745Sheppo vsw_ldc_uninit(vsw_ldc_t *ldcp) 22471ae08745Sheppo { 22481ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 22491ae08745Sheppo int rv; 22501ae08745Sheppo 22511ae08745Sheppo D1(vswp, "vsw_ldc_uninit: enter: id(%lx)\n", ldcp->ldc_id); 22521ae08745Sheppo 22531ae08745Sheppo LDC_ENTER_LOCK(ldcp); 22541ae08745Sheppo 22551ae08745Sheppo rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE); 22561ae08745Sheppo if (rv != 0) { 22571ae08745Sheppo DERR(vswp, "vsw_ldc_uninit(%lld): error disabling " 22581ae08745Sheppo "interrupts (rv = %d)\n", ldcp->ldc_id, rv); 22591ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22601ae08745Sheppo return (1); 22611ae08745Sheppo } 22621ae08745Sheppo 22631ae08745Sheppo ldcp->ldc_status = LDC_INIT; 22641ae08745Sheppo 22651ae08745Sheppo LDC_EXIT_LOCK(ldcp); 22661ae08745Sheppo 22671ae08745Sheppo D1(vswp, "vsw_ldc_uninit: exit: id(%lx)", ldcp->ldc_id); 22681ae08745Sheppo 22691ae08745Sheppo return (0); 22701ae08745Sheppo } 22711ae08745Sheppo 22721ae08745Sheppo static int 22731ae08745Sheppo vsw_init_ldcs(vsw_port_t *port) 22741ae08745Sheppo { 22751ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 22761ae08745Sheppo vsw_ldc_t *ldcp; 22771ae08745Sheppo 22781ae08745Sheppo READ_ENTER(&ldcl->lockrw); 22791ae08745Sheppo ldcp = ldcl->head; 22801ae08745Sheppo for (; ldcp != NULL; ldcp = ldcp->ldc_next) { 22811ae08745Sheppo (void) vsw_ldc_init(ldcp); 22821ae08745Sheppo } 22831ae08745Sheppo RW_EXIT(&ldcl->lockrw); 22841ae08745Sheppo 22851ae08745Sheppo return (0); 22861ae08745Sheppo } 22871ae08745Sheppo 22881ae08745Sheppo static int 22891ae08745Sheppo vsw_uninit_ldcs(vsw_port_t *port) 22901ae08745Sheppo { 22911ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 22921ae08745Sheppo vsw_ldc_t *ldcp; 22931ae08745Sheppo 22941ae08745Sheppo D1(NULL, "vsw_uninit_ldcs: enter\n"); 22951ae08745Sheppo 22961ae08745Sheppo READ_ENTER(&ldcl->lockrw); 22971ae08745Sheppo ldcp = ldcl->head; 22981ae08745Sheppo for (; ldcp != NULL; ldcp = ldcp->ldc_next) { 22991ae08745Sheppo (void) vsw_ldc_uninit(ldcp); 23001ae08745Sheppo } 23011ae08745Sheppo RW_EXIT(&ldcl->lockrw); 23021ae08745Sheppo 23031ae08745Sheppo D1(NULL, "vsw_uninit_ldcs: exit\n"); 23041ae08745Sheppo 23051ae08745Sheppo return (0); 23061ae08745Sheppo } 23071ae08745Sheppo 23081ae08745Sheppo /* 23091ae08745Sheppo * Wait until the callback(s) associated with the ldcs under the specified 23101ae08745Sheppo * port have completed. 23111ae08745Sheppo * 23121ae08745Sheppo * Prior to this function being invoked each channel under this port 23131ae08745Sheppo * should have been quiesced via ldc_set_cb_mode(DISABLE). 23141ae08745Sheppo * 23151ae08745Sheppo * A short explaination of what we are doing below.. 23161ae08745Sheppo * 23171ae08745Sheppo * The simplest approach would be to have a reference counter in 23181ae08745Sheppo * the ldc structure which is increment/decremented by the callbacks as 23191ae08745Sheppo * they use the channel. The drain function could then simply disable any 23201ae08745Sheppo * further callbacks and do a cv_wait for the ref to hit zero. Unfortunately 23211ae08745Sheppo * there is a tiny window here - before the callback is able to get the lock 23221ae08745Sheppo * on the channel it is interrupted and this function gets to execute. It 23231ae08745Sheppo * sees that the ref count is zero and believes its free to delete the 23241ae08745Sheppo * associated data structures. 23251ae08745Sheppo * 23261ae08745Sheppo * We get around this by taking advantage of the fact that before the ldc 23271ae08745Sheppo * framework invokes a callback it sets a flag to indicate that there is a 23281ae08745Sheppo * callback active (or about to become active). If when we attempt to 23291ae08745Sheppo * unregister a callback when this active flag is set then the unregister 23301ae08745Sheppo * will fail with EWOULDBLOCK. 23311ae08745Sheppo * 23321ae08745Sheppo * If the unregister fails we do a cv_timedwait. We will either be signaled 23331ae08745Sheppo * by the callback as it is exiting (note we have to wait a short period to 23341ae08745Sheppo * allow the callback to return fully to the ldc framework and it to clear 23351ae08745Sheppo * the active flag), or by the timer expiring. In either case we again attempt 23361ae08745Sheppo * the unregister. We repeat this until we can succesfully unregister the 23371ae08745Sheppo * callback. 23381ae08745Sheppo * 23391ae08745Sheppo * The reason we use a cv_timedwait rather than a simple cv_wait is to catch 23401ae08745Sheppo * the case where the callback has finished but the ldc framework has not yet 23411ae08745Sheppo * cleared the active flag. In this case we would never get a cv_signal. 23421ae08745Sheppo */ 23431ae08745Sheppo static int 23441ae08745Sheppo vsw_drain_ldcs(vsw_port_t *port) 23451ae08745Sheppo { 23461ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 23471ae08745Sheppo vsw_ldc_t *ldcp; 23481ae08745Sheppo vsw_t *vswp = port->p_vswp; 23491ae08745Sheppo 23501ae08745Sheppo D1(vswp, "%s: enter", __func__); 23511ae08745Sheppo 23521ae08745Sheppo READ_ENTER(&ldcl->lockrw); 23531ae08745Sheppo 23541ae08745Sheppo ldcp = ldcl->head; 23551ae08745Sheppo 23561ae08745Sheppo for (; ldcp != NULL; ldcp = ldcp->ldc_next) { 23571ae08745Sheppo /* 23581ae08745Sheppo * If we can unregister the channel callback then we 23591ae08745Sheppo * know that there is no callback either running or 23601ae08745Sheppo * scheduled to run for this channel so move on to next 23611ae08745Sheppo * channel in the list. 23621ae08745Sheppo */ 23631ae08745Sheppo mutex_enter(&ldcp->drain_cv_lock); 23641ae08745Sheppo 23651ae08745Sheppo /* prompt active callbacks to quit */ 23661ae08745Sheppo ldcp->drain_state = VSW_LDC_DRAINING; 23671ae08745Sheppo 23681ae08745Sheppo if ((ldc_unreg_callback(ldcp->ldc_handle)) == 0) { 23691ae08745Sheppo D2(vswp, "%s: unreg callback for chan %ld", __func__, 23701ae08745Sheppo ldcp->ldc_id); 23711ae08745Sheppo mutex_exit(&ldcp->drain_cv_lock); 23721ae08745Sheppo continue; 23731ae08745Sheppo } else { 23741ae08745Sheppo /* 23751ae08745Sheppo * If we end up here we know that either 1) a callback 23761ae08745Sheppo * is currently executing, 2) is about to start (i.e. 23771ae08745Sheppo * the ldc framework has set the active flag but 23781ae08745Sheppo * has not actually invoked the callback yet, or 3) 23791ae08745Sheppo * has finished and has returned to the ldc framework 23801ae08745Sheppo * but the ldc framework has not yet cleared the 23811ae08745Sheppo * active bit. 23821ae08745Sheppo * 23831ae08745Sheppo * Wait for it to finish. 23841ae08745Sheppo */ 23851ae08745Sheppo while (ldc_unreg_callback(ldcp->ldc_handle) 23861ae08745Sheppo == EWOULDBLOCK) 23871ae08745Sheppo (void) cv_timedwait(&ldcp->drain_cv, 23881ae08745Sheppo &ldcp->drain_cv_lock, lbolt + hz); 23891ae08745Sheppo 23901ae08745Sheppo mutex_exit(&ldcp->drain_cv_lock); 23911ae08745Sheppo D2(vswp, "%s: unreg callback for chan %ld after " 23921ae08745Sheppo "timeout", __func__, ldcp->ldc_id); 23931ae08745Sheppo } 23941ae08745Sheppo } 23951ae08745Sheppo RW_EXIT(&ldcl->lockrw); 23961ae08745Sheppo 23971ae08745Sheppo D1(vswp, "%s: exit", __func__); 23981ae08745Sheppo return (0); 23991ae08745Sheppo } 24001ae08745Sheppo 24011ae08745Sheppo /* 24021ae08745Sheppo * Wait until all tasks which reference this port have completed. 24031ae08745Sheppo * 24041ae08745Sheppo * Prior to this function being invoked each channel under this port 24051ae08745Sheppo * should have been quiesced via ldc_set_cb_mode(DISABLE). 24061ae08745Sheppo */ 24071ae08745Sheppo static int 24081ae08745Sheppo vsw_drain_port_taskq(vsw_port_t *port) 24091ae08745Sheppo { 24101ae08745Sheppo vsw_t *vswp = port->p_vswp; 24111ae08745Sheppo 24121ae08745Sheppo D1(vswp, "%s: enter", __func__); 24131ae08745Sheppo 24141ae08745Sheppo /* 24151ae08745Sheppo * Mark the port as in the process of being detached, and 24161ae08745Sheppo * dispatch a marker task to the queue so we know when all 24171ae08745Sheppo * relevant tasks have completed. 24181ae08745Sheppo */ 24191ae08745Sheppo mutex_enter(&port->state_lock); 24201ae08745Sheppo port->state = VSW_PORT_DETACHING; 24211ae08745Sheppo 24221ae08745Sheppo if ((vswp->taskq_p == NULL) || 24231ae08745Sheppo (ddi_taskq_dispatch(vswp->taskq_p, vsw_marker_task, 24241ae08745Sheppo port, DDI_NOSLEEP) != DDI_SUCCESS)) { 24251ae08745Sheppo DERR(vswp, "%s: unable to dispatch marker task", 24261ae08745Sheppo __func__); 24271ae08745Sheppo mutex_exit(&port->state_lock); 24281ae08745Sheppo return (1); 24291ae08745Sheppo } 24301ae08745Sheppo 24311ae08745Sheppo /* 24321ae08745Sheppo * Wait for the marker task to finish. 24331ae08745Sheppo */ 24341ae08745Sheppo while (port->state != VSW_PORT_DETACHABLE) 24351ae08745Sheppo cv_wait(&port->state_cv, &port->state_lock); 24361ae08745Sheppo 24371ae08745Sheppo mutex_exit(&port->state_lock); 24381ae08745Sheppo 24391ae08745Sheppo D1(vswp, "%s: exit", __func__); 24401ae08745Sheppo 24411ae08745Sheppo return (0); 24421ae08745Sheppo } 24431ae08745Sheppo 24441ae08745Sheppo static void 24451ae08745Sheppo vsw_marker_task(void *arg) 24461ae08745Sheppo { 24471ae08745Sheppo vsw_port_t *port = arg; 24481ae08745Sheppo vsw_t *vswp = port->p_vswp; 24491ae08745Sheppo 24501ae08745Sheppo D1(vswp, "%s: enter", __func__); 24511ae08745Sheppo 24521ae08745Sheppo mutex_enter(&port->state_lock); 24531ae08745Sheppo 24541ae08745Sheppo /* 24551ae08745Sheppo * No further tasks should be dispatched which reference 24561ae08745Sheppo * this port so ok to mark it as safe to detach. 24571ae08745Sheppo */ 24581ae08745Sheppo port->state = VSW_PORT_DETACHABLE; 24591ae08745Sheppo 24601ae08745Sheppo cv_signal(&port->state_cv); 24611ae08745Sheppo 24621ae08745Sheppo mutex_exit(&port->state_lock); 24631ae08745Sheppo 24641ae08745Sheppo D1(vswp, "%s: exit", __func__); 24651ae08745Sheppo } 24661ae08745Sheppo 24671ae08745Sheppo static vsw_port_t * 24681ae08745Sheppo vsw_lookup_port(vsw_t *vswp, int p_instance) 24691ae08745Sheppo { 24701ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 24711ae08745Sheppo vsw_port_t *port; 24721ae08745Sheppo 24731ae08745Sheppo for (port = plist->head; port != NULL; port = port->p_next) { 24741ae08745Sheppo if (port->p_instance == p_instance) { 24751ae08745Sheppo D2(vswp, "vsw_lookup_port: found p_instance\n"); 24761ae08745Sheppo return (port); 24771ae08745Sheppo } 24781ae08745Sheppo } 24791ae08745Sheppo 24801ae08745Sheppo return (NULL); 24811ae08745Sheppo } 24821ae08745Sheppo 24831ae08745Sheppo /* 24841ae08745Sheppo * Search for and remove the specified port from the port 24851ae08745Sheppo * list. Returns 0 if able to locate and remove port, otherwise 24861ae08745Sheppo * returns 1. 24871ae08745Sheppo */ 24881ae08745Sheppo static int 24891ae08745Sheppo vsw_plist_del_node(vsw_t *vswp, vsw_port_t *port) 24901ae08745Sheppo { 24911ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 24921ae08745Sheppo vsw_port_t *curr_p, *prev_p; 24931ae08745Sheppo 24941ae08745Sheppo if (plist->head == NULL) 24951ae08745Sheppo return (1); 24961ae08745Sheppo 24971ae08745Sheppo curr_p = prev_p = plist->head; 24981ae08745Sheppo 24991ae08745Sheppo while (curr_p != NULL) { 25001ae08745Sheppo if (curr_p == port) { 25011ae08745Sheppo if (prev_p == curr_p) { 25021ae08745Sheppo plist->head = curr_p->p_next; 25031ae08745Sheppo } else { 25041ae08745Sheppo prev_p->p_next = curr_p->p_next; 25051ae08745Sheppo } 25061ae08745Sheppo plist->num_ports--; 25071ae08745Sheppo break; 25081ae08745Sheppo } else { 25091ae08745Sheppo prev_p = curr_p; 25101ae08745Sheppo curr_p = curr_p->p_next; 25111ae08745Sheppo } 25121ae08745Sheppo } 25131ae08745Sheppo return (0); 25141ae08745Sheppo } 25151ae08745Sheppo 25161ae08745Sheppo /* 25171ae08745Sheppo * Interrupt handler for ldc messages. 25181ae08745Sheppo */ 25191ae08745Sheppo static uint_t 25201ae08745Sheppo vsw_ldc_cb(uint64_t event, caddr_t arg) 25211ae08745Sheppo { 25221ae08745Sheppo vsw_ldc_t *ldcp = (vsw_ldc_t *)arg; 25231ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 25241ae08745Sheppo ldc_status_t lstatus; 25251ae08745Sheppo int rv; 25261ae08745Sheppo 25271ae08745Sheppo D1(vswp, "%s: enter: ldcid (%lld)\n", __func__, ldcp->ldc_id); 25281ae08745Sheppo 25291ae08745Sheppo mutex_enter(&ldcp->ldc_cblock); 25301ae08745Sheppo 25311ae08745Sheppo if ((ldcp->ldc_status == LDC_INIT) || (ldcp->ldc_handle == NULL)) { 25321ae08745Sheppo mutex_exit(&ldcp->ldc_cblock); 25331ae08745Sheppo return (LDC_SUCCESS); 25341ae08745Sheppo } 25351ae08745Sheppo 25361ae08745Sheppo if (event & LDC_EVT_UP) { 25371ae08745Sheppo /* 25381ae08745Sheppo * Channel has come up, get the state and then start 25391ae08745Sheppo * the handshake. 25401ae08745Sheppo */ 25411ae08745Sheppo rv = ldc_status(ldcp->ldc_handle, &lstatus); 25421ae08745Sheppo if (rv != 0) { 25431ae08745Sheppo cmn_err(CE_WARN, "Unable to read channel state"); 25441ae08745Sheppo } 25451ae08745Sheppo ldcp->ldc_status = lstatus; 25461ae08745Sheppo 25471ae08745Sheppo D2(vswp, "%s: id(%ld) event(%llx) UP: status(%ld)", 25481ae08745Sheppo __func__, ldcp->ldc_id, event, ldcp->ldc_status); 25491ae08745Sheppo 25501ae08745Sheppo vsw_restart_handshake(ldcp); 25511ae08745Sheppo 25521ae08745Sheppo ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0); 25531ae08745Sheppo } 25541ae08745Sheppo 25551ae08745Sheppo if (event & LDC_EVT_READ) { 25561ae08745Sheppo /* 25571ae08745Sheppo * Data available for reading. 25581ae08745Sheppo */ 25591ae08745Sheppo D2(vswp, "%s: id(ld) event(%llx) data READ", 25601ae08745Sheppo __func__, ldcp->ldc_id, event); 25611ae08745Sheppo 25621ae08745Sheppo vsw_process_pkt(ldcp); 25631ae08745Sheppo 25641ae08745Sheppo ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0); 25651ae08745Sheppo 25661ae08745Sheppo goto vsw_cb_exit; 25671ae08745Sheppo } 25681ae08745Sheppo 25691ae08745Sheppo if (event & LDC_EVT_RESET) { 25701ae08745Sheppo rv = ldc_status(ldcp->ldc_handle, &lstatus); 25711ae08745Sheppo if (rv != 0) { 25721ae08745Sheppo cmn_err(CE_WARN, "Unable to read channel state"); 25731ae08745Sheppo } else { 25741ae08745Sheppo ldcp->ldc_status = lstatus; 25751ae08745Sheppo } 25761ae08745Sheppo D2(vswp, "%s: id(%ld) event(%llx) RESET: status (%ld)", 25771ae08745Sheppo __func__, ldcp->ldc_id, event, ldcp->ldc_status); 25781ae08745Sheppo } 25791ae08745Sheppo 25801ae08745Sheppo if (event & LDC_EVT_DOWN) { 25811ae08745Sheppo rv = ldc_status(ldcp->ldc_handle, &lstatus); 25821ae08745Sheppo if (rv != 0) { 25831ae08745Sheppo cmn_err(CE_WARN, "Unable to read channel state"); 25841ae08745Sheppo } else { 25851ae08745Sheppo ldcp->ldc_status = lstatus; 25861ae08745Sheppo } 25871ae08745Sheppo 25881ae08745Sheppo D2(vswp, "%s: id(%ld) event(%llx) DOWN: status (%ld)", 25891ae08745Sheppo __func__, ldcp->ldc_id, event, ldcp->ldc_status); 25901ae08745Sheppo 25911ae08745Sheppo } 25921ae08745Sheppo 25931ae08745Sheppo /* 25941ae08745Sheppo * Catch either LDC_EVT_WRITE which we don't support or any 25951ae08745Sheppo * unknown event. 25961ae08745Sheppo */ 25971ae08745Sheppo if (event & ~(LDC_EVT_UP | LDC_EVT_RESET 25981ae08745Sheppo | LDC_EVT_DOWN | LDC_EVT_READ)) { 25991ae08745Sheppo 26001ae08745Sheppo DERR(vswp, "%s: id(%ld) Unexpected event=(%llx) status(%ld)", 26011ae08745Sheppo __func__, ldcp->ldc_id, event, ldcp->ldc_status); 26021ae08745Sheppo } 26031ae08745Sheppo 26041ae08745Sheppo vsw_cb_exit: 26051ae08745Sheppo mutex_exit(&ldcp->ldc_cblock); 26061ae08745Sheppo 26071ae08745Sheppo /* 26081ae08745Sheppo * Let the drain function know we are finishing if it 26091ae08745Sheppo * is waiting. 26101ae08745Sheppo */ 26111ae08745Sheppo mutex_enter(&ldcp->drain_cv_lock); 26121ae08745Sheppo if (ldcp->drain_state == VSW_LDC_DRAINING) 26131ae08745Sheppo cv_signal(&ldcp->drain_cv); 26141ae08745Sheppo mutex_exit(&ldcp->drain_cv_lock); 26151ae08745Sheppo 26161ae08745Sheppo return (LDC_SUCCESS); 26171ae08745Sheppo } 26181ae08745Sheppo 26191ae08745Sheppo /* 26201ae08745Sheppo * (Re)start a handshake with our peer by sending them 26211ae08745Sheppo * our version info. 26221ae08745Sheppo */ 26231ae08745Sheppo static void 26241ae08745Sheppo vsw_restart_handshake(vsw_ldc_t *ldcp) 26251ae08745Sheppo { 26261ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 26271ae08745Sheppo vsw_port_t *port; 26281ae08745Sheppo vsw_ldc_list_t *ldcl; 26291ae08745Sheppo 26301ae08745Sheppo D1(vswp, "vsw_restart_handshake: enter"); 26311ae08745Sheppo 26321ae08745Sheppo port = ldcp->ldc_port; 26331ae08745Sheppo ldcl = &port->p_ldclist; 26341ae08745Sheppo 26351ae08745Sheppo WRITE_ENTER(&ldcl->lockrw); 26361ae08745Sheppo 26371ae08745Sheppo D2(vswp, "%s: in 0x%llx : out 0x%llx", __func__, 26381ae08745Sheppo ldcp->lane_in.lstate, ldcp->lane_out.lstate); 26391ae08745Sheppo 26401ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 26411ae08745Sheppo vsw_free_lane_resources(ldcp, OUTBOUND); 26421ae08745Sheppo RW_EXIT(&ldcl->lockrw); 26431ae08745Sheppo 26441ae08745Sheppo ldcp->lane_in.lstate = 0; 26451ae08745Sheppo ldcp->lane_out.lstate = 0; 26461ae08745Sheppo 26471ae08745Sheppo /* 26481ae08745Sheppo * Remove parent port from any multicast groups 26491ae08745Sheppo * it may have registered with. Client must resend 26501ae08745Sheppo * multicast add command after handshake completes. 26511ae08745Sheppo */ 26521ae08745Sheppo (void) vsw_del_fdb(vswp, port); 26531ae08745Sheppo 26541ae08745Sheppo vsw_del_mcst_port(port); 26551ae08745Sheppo 26561ae08745Sheppo ldcp->hphase = VSW_MILESTONE0; 26571ae08745Sheppo 26581ae08745Sheppo ldcp->peer_session = 0; 26591ae08745Sheppo ldcp->session_status = 0; 26601ae08745Sheppo 26611ae08745Sheppo /* 26621ae08745Sheppo * We now increment the transaction group id. This allows 26631ae08745Sheppo * us to identify and disard any tasks which are still pending 26641ae08745Sheppo * on the taskq and refer to the handshake session we are about 26651ae08745Sheppo * to restart. These stale messages no longer have any real 26661ae08745Sheppo * meaning. 26671ae08745Sheppo */ 26681ae08745Sheppo mutex_enter(&ldcp->hss_lock); 26691ae08745Sheppo ldcp->hss_id++; 26701ae08745Sheppo mutex_exit(&ldcp->hss_lock); 26711ae08745Sheppo 26721ae08745Sheppo if (ldcp->hcnt++ > vsw_num_handshakes) { 26731ae08745Sheppo cmn_err(CE_WARN, "exceeded number of permitted " 26741ae08745Sheppo "handshake attempts (%d) on channel %ld", 26751ae08745Sheppo ldcp->hcnt, ldcp->ldc_id); 26761ae08745Sheppo return; 26771ae08745Sheppo } 26781ae08745Sheppo 26791ae08745Sheppo vsw_send_ver(ldcp); 26801ae08745Sheppo 26811ae08745Sheppo D1(vswp, "vsw_restart_handshake: exit"); 26821ae08745Sheppo } 26831ae08745Sheppo 26841ae08745Sheppo /* 26851ae08745Sheppo * returns 0 if legal for event signified by flag to have 26861ae08745Sheppo * occured at the time it did. Otherwise returns 1. 26871ae08745Sheppo */ 26881ae08745Sheppo int 26891ae08745Sheppo vsw_check_flag(vsw_ldc_t *ldcp, int dir, uint64_t flag) 26901ae08745Sheppo { 26911ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 26921ae08745Sheppo uint64_t state; 26931ae08745Sheppo uint64_t phase; 26941ae08745Sheppo 26951ae08745Sheppo if (dir == INBOUND) 26961ae08745Sheppo state = ldcp->lane_in.lstate; 26971ae08745Sheppo else 26981ae08745Sheppo state = ldcp->lane_out.lstate; 26991ae08745Sheppo 27001ae08745Sheppo phase = ldcp->hphase; 27011ae08745Sheppo 27021ae08745Sheppo switch (flag) { 27031ae08745Sheppo case VSW_VER_INFO_RECV: 27041ae08745Sheppo if (phase > VSW_MILESTONE0) { 27051ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): VER_INFO_RECV" 27061ae08745Sheppo " when in state %d\n", ldcp->ldc_id, phase); 27071ae08745Sheppo vsw_restart_handshake(ldcp); 27081ae08745Sheppo return (1); 27091ae08745Sheppo } 27101ae08745Sheppo break; 27111ae08745Sheppo 27121ae08745Sheppo case VSW_VER_ACK_RECV: 27131ae08745Sheppo case VSW_VER_NACK_RECV: 27141ae08745Sheppo if (!(state & VSW_VER_INFO_SENT)) { 27151ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): spurious VER_ACK" 27161ae08745Sheppo " or VER_NACK when in state %d\n", 27171ae08745Sheppo ldcp->ldc_id, phase); 27181ae08745Sheppo vsw_restart_handshake(ldcp); 27191ae08745Sheppo return (1); 27201ae08745Sheppo } else 27211ae08745Sheppo state &= ~VSW_VER_INFO_SENT; 27221ae08745Sheppo break; 27231ae08745Sheppo 27241ae08745Sheppo case VSW_ATTR_INFO_RECV: 27251ae08745Sheppo if ((phase < VSW_MILESTONE1) || (phase >= VSW_MILESTONE2)) { 27261ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): ATTR_INFO_RECV" 27271ae08745Sheppo " when in state %d\n", ldcp->ldc_id, phase); 27281ae08745Sheppo vsw_restart_handshake(ldcp); 27291ae08745Sheppo return (1); 27301ae08745Sheppo } 27311ae08745Sheppo break; 27321ae08745Sheppo 27331ae08745Sheppo case VSW_ATTR_ACK_RECV: 27341ae08745Sheppo case VSW_ATTR_NACK_RECV: 27351ae08745Sheppo if (!(state & VSW_ATTR_INFO_SENT)) { 27361ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): spurious ATTR_ACK" 27371ae08745Sheppo " or ATTR_NACK when in state %d\n", 27381ae08745Sheppo ldcp->ldc_id, phase); 27391ae08745Sheppo vsw_restart_handshake(ldcp); 27401ae08745Sheppo return (1); 27411ae08745Sheppo } else 27421ae08745Sheppo state &= ~VSW_ATTR_INFO_SENT; 27431ae08745Sheppo break; 27441ae08745Sheppo 27451ae08745Sheppo case VSW_DRING_INFO_RECV: 27461ae08745Sheppo if (phase < VSW_MILESTONE1) { 27471ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): DRING_INFO_RECV" 27481ae08745Sheppo " when in state %d\n", ldcp->ldc_id, phase); 27491ae08745Sheppo vsw_restart_handshake(ldcp); 27501ae08745Sheppo return (1); 27511ae08745Sheppo } 27521ae08745Sheppo break; 27531ae08745Sheppo 27541ae08745Sheppo case VSW_DRING_ACK_RECV: 27551ae08745Sheppo case VSW_DRING_NACK_RECV: 27561ae08745Sheppo if (!(state & VSW_DRING_INFO_SENT)) { 27571ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): spurious DRING_ACK" 27581ae08745Sheppo " or DRING_NACK when in state %d\n", 27591ae08745Sheppo ldcp->ldc_id, phase); 27601ae08745Sheppo vsw_restart_handshake(ldcp); 27611ae08745Sheppo return (1); 27621ae08745Sheppo } else 27631ae08745Sheppo state &= ~VSW_DRING_INFO_SENT; 27641ae08745Sheppo break; 27651ae08745Sheppo 27661ae08745Sheppo case VSW_RDX_INFO_RECV: 27671ae08745Sheppo if (phase < VSW_MILESTONE3) { 27681ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): RDX_INFO_RECV" 27691ae08745Sheppo " when in state %d\n", ldcp->ldc_id, phase); 27701ae08745Sheppo vsw_restart_handshake(ldcp); 27711ae08745Sheppo return (1); 27721ae08745Sheppo } 27731ae08745Sheppo break; 27741ae08745Sheppo 27751ae08745Sheppo case VSW_RDX_ACK_RECV: 27761ae08745Sheppo case VSW_RDX_NACK_RECV: 27771ae08745Sheppo if (!(state & VSW_RDX_INFO_SENT)) { 27781ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): spurious RDX_ACK" 27791ae08745Sheppo " or RDX_NACK when in state %d\n", 27801ae08745Sheppo ldcp->ldc_id, phase); 27811ae08745Sheppo vsw_restart_handshake(ldcp); 27821ae08745Sheppo return (1); 27831ae08745Sheppo } else 27841ae08745Sheppo state &= ~VSW_RDX_INFO_SENT; 27851ae08745Sheppo break; 27861ae08745Sheppo 27871ae08745Sheppo case VSW_MCST_INFO_RECV: 27881ae08745Sheppo if (phase < VSW_MILESTONE3) { 27891ae08745Sheppo DERR(vswp, "vsw_check_flag (%d): VSW_MCST_INFO_RECV" 27901ae08745Sheppo " when in state %d\n", ldcp->ldc_id, phase); 27911ae08745Sheppo vsw_restart_handshake(ldcp); 27921ae08745Sheppo return (1); 27931ae08745Sheppo } 27941ae08745Sheppo break; 27951ae08745Sheppo 27961ae08745Sheppo default: 27971ae08745Sheppo DERR(vswp, "vsw_check_flag (%lld): unknown flag (%llx)", 27981ae08745Sheppo ldcp->ldc_id, flag); 27991ae08745Sheppo return (1); 28001ae08745Sheppo } 28011ae08745Sheppo 28021ae08745Sheppo if (dir == INBOUND) 28031ae08745Sheppo ldcp->lane_in.lstate = state; 28041ae08745Sheppo else 28051ae08745Sheppo ldcp->lane_out.lstate = state; 28061ae08745Sheppo 28071ae08745Sheppo D1(vswp, "vsw_check_flag (chan %lld): exit", ldcp->ldc_id); 28081ae08745Sheppo 28091ae08745Sheppo return (0); 28101ae08745Sheppo } 28111ae08745Sheppo 28121ae08745Sheppo void 28131ae08745Sheppo vsw_next_milestone(vsw_ldc_t *ldcp) 28141ae08745Sheppo { 28151ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 28161ae08745Sheppo 28171ae08745Sheppo D1(vswp, "%s (chan %lld): enter (phase %ld)", __func__, 28181ae08745Sheppo ldcp->ldc_id, ldcp->hphase); 28191ae08745Sheppo 28201ae08745Sheppo DUMP_FLAGS(ldcp->lane_in.lstate); 28211ae08745Sheppo DUMP_FLAGS(ldcp->lane_out.lstate); 28221ae08745Sheppo 28231ae08745Sheppo switch (ldcp->hphase) { 28241ae08745Sheppo 28251ae08745Sheppo case VSW_MILESTONE0: 28261ae08745Sheppo /* 28271ae08745Sheppo * If we haven't started to handshake with our peer, 28281ae08745Sheppo * start to do so now. 28291ae08745Sheppo */ 28301ae08745Sheppo if (ldcp->lane_out.lstate == 0) { 28311ae08745Sheppo D2(vswp, "%s: (chan %lld) starting handshake " 28321ae08745Sheppo "with peer", __func__, ldcp->ldc_id); 28331ae08745Sheppo vsw_restart_handshake(ldcp); 28341ae08745Sheppo } 28351ae08745Sheppo 28361ae08745Sheppo /* 28371ae08745Sheppo * Only way to pass this milestone is to have successfully 28381ae08745Sheppo * negotiated version info. 28391ae08745Sheppo */ 28401ae08745Sheppo if ((ldcp->lane_in.lstate & VSW_VER_ACK_SENT) && 28411ae08745Sheppo (ldcp->lane_out.lstate & VSW_VER_ACK_RECV)) { 28421ae08745Sheppo 28431ae08745Sheppo D2(vswp, "%s: (chan %lld) leaving milestone 0", 28441ae08745Sheppo __func__, ldcp->ldc_id); 28451ae08745Sheppo 28461ae08745Sheppo /* 28471ae08745Sheppo * Next milestone is passed when attribute 28481ae08745Sheppo * information has been successfully exchanged. 28491ae08745Sheppo */ 28501ae08745Sheppo ldcp->hphase = VSW_MILESTONE1; 28511ae08745Sheppo vsw_send_attr(ldcp); 28521ae08745Sheppo 28531ae08745Sheppo } 28541ae08745Sheppo break; 28551ae08745Sheppo 28561ae08745Sheppo case VSW_MILESTONE1: 28571ae08745Sheppo /* 28581ae08745Sheppo * Only way to pass this milestone is to have successfully 28591ae08745Sheppo * negotiated attribute information. 28601ae08745Sheppo */ 28611ae08745Sheppo if (ldcp->lane_in.lstate & VSW_ATTR_ACK_SENT) { 28621ae08745Sheppo 28631ae08745Sheppo ldcp->hphase = VSW_MILESTONE2; 28641ae08745Sheppo 28651ae08745Sheppo /* 28661ae08745Sheppo * If the peer device has said it wishes to 28671ae08745Sheppo * use descriptor rings then we send it our ring 28681ae08745Sheppo * info, otherwise we just set up a private ring 28691ae08745Sheppo * which we use an internal buffer 28701ae08745Sheppo */ 28711ae08745Sheppo if (ldcp->lane_in.xfer_mode == VIO_DRING_MODE) 28721ae08745Sheppo vsw_send_dring_info(ldcp); 28731ae08745Sheppo } 28741ae08745Sheppo break; 28751ae08745Sheppo 28761ae08745Sheppo 28771ae08745Sheppo case VSW_MILESTONE2: 28781ae08745Sheppo /* 28791ae08745Sheppo * If peer has indicated in its attribute message that 28801ae08745Sheppo * it wishes to use descriptor rings then the only way 28811ae08745Sheppo * to pass this milestone is for us to have received 28821ae08745Sheppo * valid dring info. 28831ae08745Sheppo * 28841ae08745Sheppo * If peer is not using descriptor rings then just fall 28851ae08745Sheppo * through. 28861ae08745Sheppo */ 28871ae08745Sheppo if ((ldcp->lane_in.xfer_mode == VIO_DRING_MODE) && 28881ae08745Sheppo (!(ldcp->lane_in.lstate & VSW_DRING_ACK_SENT))) 28891ae08745Sheppo break; 28901ae08745Sheppo 28911ae08745Sheppo D2(vswp, "%s: (chan %lld) leaving milestone 2", 28921ae08745Sheppo __func__, ldcp->ldc_id); 28931ae08745Sheppo 28941ae08745Sheppo ldcp->hphase = VSW_MILESTONE3; 28951ae08745Sheppo vsw_send_rdx(ldcp); 28961ae08745Sheppo break; 28971ae08745Sheppo 28981ae08745Sheppo case VSW_MILESTONE3: 28991ae08745Sheppo /* 29001ae08745Sheppo * Pass this milestone when all paramaters have been 29011ae08745Sheppo * successfully exchanged and RDX sent in both directions. 29021ae08745Sheppo * 29031ae08745Sheppo * Mark outbound lane as available to transmit data. 29041ae08745Sheppo */ 29051ae08745Sheppo if ((ldcp->lane_in.lstate & VSW_RDX_ACK_SENT) && 29061ae08745Sheppo (ldcp->lane_out.lstate & VSW_RDX_ACK_RECV)) { 29071ae08745Sheppo 29081ae08745Sheppo D2(vswp, "%s: (chan %lld) leaving milestone 3", 29091ae08745Sheppo __func__, ldcp->ldc_id); 29101ae08745Sheppo D2(vswp, "%s: ** handshake complete **", __func__); 29111ae08745Sheppo ldcp->lane_out.lstate |= VSW_LANE_ACTIVE; 29121ae08745Sheppo ldcp->hphase = VSW_MILESTONE4; 29131ae08745Sheppo ldcp->hcnt = 0; 29141ae08745Sheppo DISPLAY_STATE(); 29151ae08745Sheppo } 29161ae08745Sheppo break; 29171ae08745Sheppo 29181ae08745Sheppo case VSW_MILESTONE4: 29191ae08745Sheppo D2(vswp, "%s: (chan %lld) in milestone 4", __func__, 29201ae08745Sheppo ldcp->ldc_id); 29211ae08745Sheppo break; 29221ae08745Sheppo 29231ae08745Sheppo default: 29241ae08745Sheppo DERR(vswp, "%s: (chan %lld) Unknown Phase %x", __func__, 29251ae08745Sheppo ldcp->ldc_id, ldcp->hphase); 29261ae08745Sheppo } 29271ae08745Sheppo 29281ae08745Sheppo D1(vswp, "%s (chan %lld): exit (phase %ld)", __func__, ldcp->ldc_id, 29291ae08745Sheppo ldcp->hphase); 29301ae08745Sheppo } 29311ae08745Sheppo 29321ae08745Sheppo /* 29331ae08745Sheppo * Check if major version is supported. 29341ae08745Sheppo * 29351ae08745Sheppo * Returns 0 if finds supported major number, and if necessary 29361ae08745Sheppo * adjusts the minor field. 29371ae08745Sheppo * 29381ae08745Sheppo * Returns 1 if can't match major number exactly. Sets mjor/minor 29391ae08745Sheppo * to next lowest support values, or to zero if no other values possible. 29401ae08745Sheppo */ 29411ae08745Sheppo static int 29421ae08745Sheppo vsw_supported_version(vio_ver_msg_t *vp) 29431ae08745Sheppo { 29441ae08745Sheppo int i; 29451ae08745Sheppo 29461ae08745Sheppo D1(NULL, "vsw_supported_version: enter"); 29471ae08745Sheppo 29481ae08745Sheppo for (i = 0; i < VSW_NUM_VER; i++) { 29491ae08745Sheppo if (vsw_versions[i].ver_major == vp->ver_major) { 29501ae08745Sheppo /* 29511ae08745Sheppo * Matching or lower major version found. Update 29521ae08745Sheppo * minor number if necessary. 29531ae08745Sheppo */ 29541ae08745Sheppo if (vp->ver_minor > vsw_versions[i].ver_minor) { 29551ae08745Sheppo D2(NULL, "%s: adjusting minor value" 29561ae08745Sheppo " from %d to %d", __func__, 29571ae08745Sheppo vp->ver_minor, 29581ae08745Sheppo vsw_versions[i].ver_minor); 29591ae08745Sheppo vp->ver_minor = vsw_versions[i].ver_minor; 29601ae08745Sheppo } 29611ae08745Sheppo 29621ae08745Sheppo return (0); 29631ae08745Sheppo } 29641ae08745Sheppo 29651ae08745Sheppo if (vsw_versions[i].ver_major < vp->ver_major) { 29661ae08745Sheppo if (vp->ver_minor > vsw_versions[i].ver_minor) { 29671ae08745Sheppo D2(NULL, "%s: adjusting minor value" 29681ae08745Sheppo " from %d to %d", __func__, 29691ae08745Sheppo vp->ver_minor, 29701ae08745Sheppo vsw_versions[i].ver_minor); 29711ae08745Sheppo vp->ver_minor = vsw_versions[i].ver_minor; 29721ae08745Sheppo } 29731ae08745Sheppo return (1); 29741ae08745Sheppo } 29751ae08745Sheppo } 29761ae08745Sheppo 29771ae08745Sheppo /* No match was possible, zero out fields */ 29781ae08745Sheppo vp->ver_major = 0; 29791ae08745Sheppo vp->ver_minor = 0; 29801ae08745Sheppo 29811ae08745Sheppo D1(NULL, "vsw_supported_version: exit"); 29821ae08745Sheppo 29831ae08745Sheppo return (1); 29841ae08745Sheppo } 29851ae08745Sheppo 29861ae08745Sheppo /* 29871ae08745Sheppo * Main routine for processing messages received over LDC. 29881ae08745Sheppo */ 29891ae08745Sheppo static void 29901ae08745Sheppo vsw_process_pkt(void *arg) 29911ae08745Sheppo { 29921ae08745Sheppo vsw_ldc_t *ldcp = (vsw_ldc_t *)arg; 29931ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 29941ae08745Sheppo size_t msglen; 29951ae08745Sheppo vio_msg_tag_t tag; 29961ae08745Sheppo def_msg_t dmsg; 29971ae08745Sheppo int rv = 0; 29981ae08745Sheppo 29991ae08745Sheppo D1(vswp, "%s enter: ldcid (%lld)\n", __func__, ldcp->ldc_id); 30001ae08745Sheppo 30011ae08745Sheppo /* 30021ae08745Sheppo * If channel is up read messages until channel is empty. 30031ae08745Sheppo */ 30041ae08745Sheppo do { 30051ae08745Sheppo msglen = sizeof (dmsg); 30061ae08745Sheppo rv = ldc_read(ldcp->ldc_handle, (caddr_t)&dmsg, &msglen); 30071ae08745Sheppo 30081ae08745Sheppo if (rv != 0) { 30091ae08745Sheppo DERR(vswp, "%s :ldc_read err id(%lld) rv(%d) " 30101ae08745Sheppo "len(%d)\n", __func__, ldcp->ldc_id, 30111ae08745Sheppo rv, msglen); 30121ae08745Sheppo break; 30131ae08745Sheppo } 30141ae08745Sheppo 30151ae08745Sheppo if (msglen == 0) { 30161ae08745Sheppo D2(vswp, "%s: ldc_read id(%lld) NODATA", __func__, 30171ae08745Sheppo ldcp->ldc_id); 30181ae08745Sheppo break; 30191ae08745Sheppo } 30201ae08745Sheppo 30211ae08745Sheppo D2(vswp, "%s: ldc_read id(%lld): msglen(%d)", __func__, 30221ae08745Sheppo ldcp->ldc_id, msglen); 30231ae08745Sheppo 30241ae08745Sheppo /* 30251ae08745Sheppo * Figure out what sort of packet we have gotten by 30261ae08745Sheppo * examining the msg tag, and then switch it appropriately. 30271ae08745Sheppo */ 30281ae08745Sheppo bcopy(&dmsg, &tag, sizeof (vio_msg_tag_t)); 30291ae08745Sheppo 30301ae08745Sheppo switch (tag.vio_msgtype) { 30311ae08745Sheppo case VIO_TYPE_CTRL: 30321ae08745Sheppo vsw_dispatch_ctrl_task(ldcp, &dmsg, tag); 30331ae08745Sheppo break; 30341ae08745Sheppo case VIO_TYPE_DATA: 30351ae08745Sheppo vsw_process_data_pkt(ldcp, &dmsg, tag); 30361ae08745Sheppo break; 30371ae08745Sheppo case VIO_TYPE_ERR: 30381ae08745Sheppo vsw_process_err_pkt(ldcp, &dmsg, tag); 30391ae08745Sheppo break; 30401ae08745Sheppo default: 30411ae08745Sheppo DERR(vswp, "%s: Unknown tag(%lx) ", __func__, 30421ae08745Sheppo "id(%lx)\n", tag.vio_msgtype, ldcp->ldc_id); 30431ae08745Sheppo break; 30441ae08745Sheppo } 30451ae08745Sheppo } while (msglen); 30461ae08745Sheppo 30471ae08745Sheppo D1(vswp, "%s exit: ldcid (%lld)\n", __func__, ldcp->ldc_id); 30481ae08745Sheppo } 30491ae08745Sheppo 30501ae08745Sheppo /* 30511ae08745Sheppo * Dispatch a task to process a VIO control message. 30521ae08745Sheppo */ 30531ae08745Sheppo static void 30541ae08745Sheppo vsw_dispatch_ctrl_task(vsw_ldc_t *ldcp, void *cpkt, vio_msg_tag_t tag) 30551ae08745Sheppo { 30561ae08745Sheppo vsw_ctrl_task_t *ctaskp = NULL; 30571ae08745Sheppo vsw_port_t *port = ldcp->ldc_port; 30581ae08745Sheppo vsw_t *vswp = port->p_vswp; 30591ae08745Sheppo 30601ae08745Sheppo D1(vswp, "%s: enter", __func__); 30611ae08745Sheppo 30621ae08745Sheppo /* 30631ae08745Sheppo * We need to handle RDX ACK messages in-band as once they 30641ae08745Sheppo * are exchanged it is possible that we will get an 30651ae08745Sheppo * immediate (legitimate) data packet. 30661ae08745Sheppo */ 30671ae08745Sheppo if ((tag.vio_subtype_env == VIO_RDX) && 30681ae08745Sheppo (tag.vio_subtype == VIO_SUBTYPE_ACK)) { 30691ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_RDX_ACK_RECV)) 30701ae08745Sheppo return; 30711ae08745Sheppo 30721ae08745Sheppo ldcp->lane_out.lstate |= VSW_RDX_ACK_RECV; 30731ae08745Sheppo vsw_next_milestone(ldcp); 30741ae08745Sheppo D2(vswp, "%s (%ld) handling RDX_ACK in place", __func__, 30751ae08745Sheppo ldcp->ldc_id); 30761ae08745Sheppo return; 30771ae08745Sheppo } 30781ae08745Sheppo 30791ae08745Sheppo ctaskp = kmem_alloc(sizeof (vsw_ctrl_task_t), KM_NOSLEEP); 30801ae08745Sheppo 30811ae08745Sheppo if (ctaskp == NULL) { 30821ae08745Sheppo DERR(vswp, "%s: unable to alloc space for ctrl" 30831ae08745Sheppo " msg", __func__); 30841ae08745Sheppo vsw_restart_handshake(ldcp); 30851ae08745Sheppo return; 30861ae08745Sheppo } 30871ae08745Sheppo 30881ae08745Sheppo ctaskp->ldcp = ldcp; 30891ae08745Sheppo bcopy((def_msg_t *)cpkt, &ctaskp->pktp, sizeof (def_msg_t)); 30901ae08745Sheppo mutex_enter(&ldcp->hss_lock); 30911ae08745Sheppo ctaskp->hss_id = ldcp->hss_id; 30921ae08745Sheppo mutex_exit(&ldcp->hss_lock); 30931ae08745Sheppo 30941ae08745Sheppo /* 30951ae08745Sheppo * Dispatch task to processing taskq if port is not in 30961ae08745Sheppo * the process of being detached. 30971ae08745Sheppo */ 30981ae08745Sheppo mutex_enter(&port->state_lock); 30991ae08745Sheppo if (port->state == VSW_PORT_INIT) { 31001ae08745Sheppo if ((vswp->taskq_p == NULL) || 31011ae08745Sheppo (ddi_taskq_dispatch(vswp->taskq_p, 31021ae08745Sheppo vsw_process_ctrl_pkt, ctaskp, DDI_NOSLEEP) 31031ae08745Sheppo != DDI_SUCCESS)) { 31041ae08745Sheppo DERR(vswp, "%s: unable to dispatch task to taskq", 31051ae08745Sheppo __func__); 31061ae08745Sheppo kmem_free(ctaskp, sizeof (vsw_ctrl_task_t)); 31071ae08745Sheppo mutex_exit(&port->state_lock); 31081ae08745Sheppo vsw_restart_handshake(ldcp); 31091ae08745Sheppo return; 31101ae08745Sheppo } 31111ae08745Sheppo } else { 31121ae08745Sheppo DWARN(vswp, "%s: port %d detaching, not dispatching " 31131ae08745Sheppo "task", __func__, port->p_instance); 31141ae08745Sheppo } 31151ae08745Sheppo 31161ae08745Sheppo mutex_exit(&port->state_lock); 31171ae08745Sheppo 31181ae08745Sheppo D2(vswp, "%s: dispatched task to taskq for chan %d", __func__, 31191ae08745Sheppo ldcp->ldc_id); 31201ae08745Sheppo D1(vswp, "%s: exit", __func__); 31211ae08745Sheppo } 31221ae08745Sheppo 31231ae08745Sheppo /* 31241ae08745Sheppo * Process a VIO ctrl message. Invoked from taskq. 31251ae08745Sheppo */ 31261ae08745Sheppo static void 31271ae08745Sheppo vsw_process_ctrl_pkt(void *arg) 31281ae08745Sheppo { 31291ae08745Sheppo vsw_ctrl_task_t *ctaskp = (vsw_ctrl_task_t *)arg; 31301ae08745Sheppo vsw_ldc_t *ldcp = ctaskp->ldcp; 31311ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 31321ae08745Sheppo vio_msg_tag_t tag; 31331ae08745Sheppo uint16_t env; 31341ae08745Sheppo 31351ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 31361ae08745Sheppo 31371ae08745Sheppo bcopy(&ctaskp->pktp, &tag, sizeof (vio_msg_tag_t)); 31381ae08745Sheppo env = tag.vio_subtype_env; 31391ae08745Sheppo 31401ae08745Sheppo /* stale pkt check */ 31411ae08745Sheppo mutex_enter(&ldcp->hss_lock); 31421ae08745Sheppo if (ctaskp->hss_id < ldcp->hss_id) { 31431ae08745Sheppo DWARN(vswp, "%s: discarding stale packet belonging to" 31441ae08745Sheppo " earlier (%ld) handshake session", __func__, 31451ae08745Sheppo ctaskp->hss_id); 31461ae08745Sheppo mutex_exit(&ldcp->hss_lock); 31471ae08745Sheppo return; 31481ae08745Sheppo } 31491ae08745Sheppo mutex_exit(&ldcp->hss_lock); 31501ae08745Sheppo 31511ae08745Sheppo /* session id check */ 31521ae08745Sheppo if (ldcp->session_status & VSW_PEER_SESSION) { 31531ae08745Sheppo if (ldcp->peer_session != tag.vio_sid) { 31541ae08745Sheppo DERR(vswp, "%s (chan %d): invalid session id (%llx)", 31551ae08745Sheppo __func__, ldcp->ldc_id, tag.vio_sid); 31561ae08745Sheppo kmem_free(ctaskp, sizeof (vsw_ctrl_task_t)); 31571ae08745Sheppo vsw_restart_handshake(ldcp); 31581ae08745Sheppo return; 31591ae08745Sheppo } 31601ae08745Sheppo } 31611ae08745Sheppo 31621ae08745Sheppo /* 31631ae08745Sheppo * Switch on vio_subtype envelope, then let lower routines 31641ae08745Sheppo * decide if its an INFO, ACK or NACK packet. 31651ae08745Sheppo */ 31661ae08745Sheppo switch (env) { 31671ae08745Sheppo case VIO_VER_INFO: 31681ae08745Sheppo vsw_process_ctrl_ver_pkt(ldcp, &ctaskp->pktp); 31691ae08745Sheppo break; 31701ae08745Sheppo case VIO_DRING_REG: 31711ae08745Sheppo vsw_process_ctrl_dring_reg_pkt(ldcp, &ctaskp->pktp); 31721ae08745Sheppo break; 31731ae08745Sheppo case VIO_DRING_UNREG: 31741ae08745Sheppo vsw_process_ctrl_dring_unreg_pkt(ldcp, &ctaskp->pktp); 31751ae08745Sheppo break; 31761ae08745Sheppo case VIO_ATTR_INFO: 31771ae08745Sheppo vsw_process_ctrl_attr_pkt(ldcp, &ctaskp->pktp); 31781ae08745Sheppo break; 31791ae08745Sheppo case VNET_MCAST_INFO: 31801ae08745Sheppo vsw_process_ctrl_mcst_pkt(ldcp, &ctaskp->pktp); 31811ae08745Sheppo break; 31821ae08745Sheppo case VIO_RDX: 31831ae08745Sheppo vsw_process_ctrl_rdx_pkt(ldcp, &ctaskp->pktp); 31841ae08745Sheppo break; 31851ae08745Sheppo default: 31861ae08745Sheppo DERR(vswp, "%s : unknown vio_subtype_env (%x)\n", 31871ae08745Sheppo __func__, env); 31881ae08745Sheppo } 31891ae08745Sheppo 31901ae08745Sheppo kmem_free(ctaskp, sizeof (vsw_ctrl_task_t)); 31911ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 31921ae08745Sheppo } 31931ae08745Sheppo 31941ae08745Sheppo /* 31951ae08745Sheppo * Version negotiation. We can end up here either because our peer 31961ae08745Sheppo * has responded to a handshake message we have sent it, or our peer 31971ae08745Sheppo * has initiated a handshake with us. If its the former then can only 31981ae08745Sheppo * be ACK or NACK, if its the later can only be INFO. 31991ae08745Sheppo * 32001ae08745Sheppo * If its an ACK we move to the next stage of the handshake, namely 32011ae08745Sheppo * attribute exchange. If its a NACK we see if we can specify another 32021ae08745Sheppo * version, if we can't we stop. 32031ae08745Sheppo * 32041ae08745Sheppo * If it is an INFO we reset all params associated with communication 32051ae08745Sheppo * in that direction over this channel (remember connection is 32061ae08745Sheppo * essentially 2 independent simplex channels). 32071ae08745Sheppo */ 32081ae08745Sheppo void 32091ae08745Sheppo vsw_process_ctrl_ver_pkt(vsw_ldc_t *ldcp, void *pkt) 32101ae08745Sheppo { 32111ae08745Sheppo vio_ver_msg_t *ver_pkt; 32121ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 32131ae08745Sheppo 32141ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 32151ae08745Sheppo 32161ae08745Sheppo /* 32171ae08745Sheppo * We know this is a ctrl/version packet so 32181ae08745Sheppo * cast it into the correct structure. 32191ae08745Sheppo */ 32201ae08745Sheppo ver_pkt = (vio_ver_msg_t *)pkt; 32211ae08745Sheppo 32221ae08745Sheppo switch (ver_pkt->tag.vio_subtype) { 32231ae08745Sheppo case VIO_SUBTYPE_INFO: 32241ae08745Sheppo D2(vswp, "vsw_process_ctrl_ver_pkt: VIO_SUBTYPE_INFO\n"); 32251ae08745Sheppo 32261ae08745Sheppo /* 32271ae08745Sheppo * Record the session id, which we will use from now 32281ae08745Sheppo * until we see another VER_INFO msg. Even then the 32291ae08745Sheppo * session id in most cases will be unchanged, execpt 32301ae08745Sheppo * if channel was reset. 32311ae08745Sheppo */ 32321ae08745Sheppo if ((ldcp->session_status & VSW_PEER_SESSION) && 32331ae08745Sheppo (ldcp->peer_session != ver_pkt->tag.vio_sid)) { 32341ae08745Sheppo DERR(vswp, "%s: updating session id for chan %lld " 32351ae08745Sheppo "from %llx to %llx", __func__, ldcp->ldc_id, 32361ae08745Sheppo ldcp->peer_session, ver_pkt->tag.vio_sid); 32371ae08745Sheppo } 32381ae08745Sheppo 32391ae08745Sheppo ldcp->peer_session = ver_pkt->tag.vio_sid; 32401ae08745Sheppo ldcp->session_status |= VSW_PEER_SESSION; 32411ae08745Sheppo 32421ae08745Sheppo /* Legal message at this time ? */ 32431ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_VER_INFO_RECV)) 32441ae08745Sheppo return; 32451ae08745Sheppo 32461ae08745Sheppo /* 32471ae08745Sheppo * First check the device class. Currently only expect 32481ae08745Sheppo * to be talking to a network device. In the future may 32491ae08745Sheppo * also talk to another switch. 32501ae08745Sheppo */ 32511ae08745Sheppo if (ver_pkt->dev_class != VDEV_NETWORK) { 32521ae08745Sheppo DERR(vswp, "%s: illegal device class %d", __func__, 32531ae08745Sheppo ver_pkt->dev_class); 32541ae08745Sheppo 32551ae08745Sheppo ver_pkt->tag.vio_sid = ldcp->local_session; 32561ae08745Sheppo ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 32571ae08745Sheppo 32581ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt); 32591ae08745Sheppo 32601ae08745Sheppo vsw_send_msg(ldcp, (void *)ver_pkt, 32611ae08745Sheppo sizeof (vio_ver_msg_t)); 32621ae08745Sheppo 32631ae08745Sheppo ldcp->lane_in.lstate |= VSW_VER_NACK_SENT; 32641ae08745Sheppo vsw_next_milestone(ldcp); 32651ae08745Sheppo return; 32661ae08745Sheppo } else { 32671ae08745Sheppo ldcp->dev_class = ver_pkt->dev_class; 32681ae08745Sheppo } 32691ae08745Sheppo 32701ae08745Sheppo /* 32711ae08745Sheppo * Now check the version. 32721ae08745Sheppo */ 32731ae08745Sheppo if (vsw_supported_version(ver_pkt) == 0) { 32741ae08745Sheppo /* 32751ae08745Sheppo * Support this major version and possibly 32761ae08745Sheppo * adjusted minor version. 32771ae08745Sheppo */ 32781ae08745Sheppo 32791ae08745Sheppo D2(vswp, "%s: accepted ver %d:%d", __func__, 32801ae08745Sheppo ver_pkt->ver_major, ver_pkt->ver_minor); 32811ae08745Sheppo 32821ae08745Sheppo /* Store accepted values */ 32831ae08745Sheppo ldcp->lane_in.ver_major = ver_pkt->ver_major; 32841ae08745Sheppo ldcp->lane_in.ver_minor = ver_pkt->ver_minor; 32851ae08745Sheppo 32861ae08745Sheppo ver_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 32871ae08745Sheppo 32881ae08745Sheppo ldcp->lane_in.lstate |= VSW_VER_ACK_SENT; 32891ae08745Sheppo } else { 32901ae08745Sheppo /* 32911ae08745Sheppo * NACK back with the next lower major/minor 32921ae08745Sheppo * pairing we support (if don't suuport any more 32931ae08745Sheppo * versions then they will be set to zero. 32941ae08745Sheppo */ 32951ae08745Sheppo 32961ae08745Sheppo D2(vswp, "%s: replying with ver %d:%d", __func__, 32971ae08745Sheppo ver_pkt->ver_major, ver_pkt->ver_minor); 32981ae08745Sheppo 32991ae08745Sheppo /* Store updated values */ 33001ae08745Sheppo ldcp->lane_in.ver_major = ver_pkt->ver_major; 33011ae08745Sheppo ldcp->lane_in.ver_minor = ver_pkt->ver_minor; 33021ae08745Sheppo 33031ae08745Sheppo ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 33041ae08745Sheppo 33051ae08745Sheppo ldcp->lane_in.lstate |= VSW_VER_NACK_SENT; 33061ae08745Sheppo } 33071ae08745Sheppo 33081ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt); 33091ae08745Sheppo ver_pkt->tag.vio_sid = ldcp->local_session; 33101ae08745Sheppo vsw_send_msg(ldcp, (void *)ver_pkt, sizeof (vio_ver_msg_t)); 33111ae08745Sheppo 33121ae08745Sheppo vsw_next_milestone(ldcp); 33131ae08745Sheppo break; 33141ae08745Sheppo 33151ae08745Sheppo case VIO_SUBTYPE_ACK: 33161ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_ACK\n", __func__); 33171ae08745Sheppo 33181ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_ACK_RECV)) 33191ae08745Sheppo return; 33201ae08745Sheppo 33211ae08745Sheppo /* Store updated values */ 33221ae08745Sheppo ldcp->lane_in.ver_major = ver_pkt->ver_major; 33231ae08745Sheppo ldcp->lane_in.ver_minor = ver_pkt->ver_minor; 33241ae08745Sheppo 33251ae08745Sheppo 33261ae08745Sheppo ldcp->lane_out.lstate |= VSW_VER_ACK_RECV; 33271ae08745Sheppo vsw_next_milestone(ldcp); 33281ae08745Sheppo 33291ae08745Sheppo break; 33301ae08745Sheppo 33311ae08745Sheppo case VIO_SUBTYPE_NACK: 33321ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_NACK\n", __func__); 33331ae08745Sheppo 33341ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_NACK_RECV)) 33351ae08745Sheppo return; 33361ae08745Sheppo 33371ae08745Sheppo /* 33381ae08745Sheppo * If our peer sent us a NACK with the ver fields set to 33391ae08745Sheppo * zero then there is nothing more we can do. Otherwise see 33401ae08745Sheppo * if we support either the version suggested, or a lesser 33411ae08745Sheppo * one. 33421ae08745Sheppo */ 33431ae08745Sheppo if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) { 33441ae08745Sheppo DERR(vswp, "%s: peer unable to negotiate any " 33451ae08745Sheppo "further.", __func__); 33461ae08745Sheppo ldcp->lane_out.lstate |= VSW_VER_NACK_RECV; 33471ae08745Sheppo vsw_next_milestone(ldcp); 33481ae08745Sheppo return; 33491ae08745Sheppo } 33501ae08745Sheppo 33511ae08745Sheppo /* 33521ae08745Sheppo * Check to see if we support this major version or 33531ae08745Sheppo * a lower one. If we don't then maj/min will be set 33541ae08745Sheppo * to zero. 33551ae08745Sheppo */ 33561ae08745Sheppo (void) vsw_supported_version(ver_pkt); 33571ae08745Sheppo if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) { 33581ae08745Sheppo /* Nothing more we can do */ 33591ae08745Sheppo DERR(vswp, "%s: version negotiation failed.\n", 33601ae08745Sheppo __func__); 33611ae08745Sheppo ldcp->lane_out.lstate |= VSW_VER_NACK_RECV; 33621ae08745Sheppo vsw_next_milestone(ldcp); 33631ae08745Sheppo } else { 33641ae08745Sheppo /* found a supported major version */ 33651ae08745Sheppo ldcp->lane_out.ver_major = ver_pkt->ver_major; 33661ae08745Sheppo ldcp->lane_out.ver_minor = ver_pkt->ver_minor; 33671ae08745Sheppo 33681ae08745Sheppo D2(vswp, "%s: resending with updated values (%x, %x)", 33691ae08745Sheppo __func__, ver_pkt->ver_major, 33701ae08745Sheppo ver_pkt->ver_minor); 33711ae08745Sheppo 33721ae08745Sheppo ldcp->lane_out.lstate |= VSW_VER_INFO_SENT; 33731ae08745Sheppo ver_pkt->tag.vio_sid = ldcp->local_session; 33741ae08745Sheppo ver_pkt->tag.vio_subtype = VIO_SUBTYPE_INFO; 33751ae08745Sheppo 33761ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt); 33771ae08745Sheppo 33781ae08745Sheppo vsw_send_msg(ldcp, (void *)ver_pkt, 33791ae08745Sheppo sizeof (vio_ver_msg_t)); 33801ae08745Sheppo 33811ae08745Sheppo vsw_next_milestone(ldcp); 33821ae08745Sheppo 33831ae08745Sheppo } 33841ae08745Sheppo break; 33851ae08745Sheppo 33861ae08745Sheppo default: 33871ae08745Sheppo DERR(vswp, "%s: unknown vio_subtype %x\n", __func__, 33881ae08745Sheppo ver_pkt->tag.vio_subtype); 33891ae08745Sheppo } 33901ae08745Sheppo 33911ae08745Sheppo D1(vswp, "%s(%lld): exit\n", __func__, ldcp->ldc_id); 33921ae08745Sheppo } 33931ae08745Sheppo 33941ae08745Sheppo /* 33951ae08745Sheppo * Process an attribute packet. We can end up here either because our peer 33961ae08745Sheppo * has ACK/NACK'ed back to an earlier ATTR msg we had sent it, or our 33971ae08745Sheppo * peer has sent us an attribute INFO message 33981ae08745Sheppo * 33991ae08745Sheppo * If its an ACK we then move to the next stage of the handshake which 34001ae08745Sheppo * is to send our descriptor ring info to our peer. If its a NACK then 34011ae08745Sheppo * there is nothing more we can (currently) do. 34021ae08745Sheppo * 34031ae08745Sheppo * If we get a valid/acceptable INFO packet (and we have already negotiated 34041ae08745Sheppo * a version) we ACK back and set channel state to ATTR_RECV, otherwise we 34051ae08745Sheppo * NACK back and reset channel state to INACTIV. 34061ae08745Sheppo * 34071ae08745Sheppo * FUTURE: in time we will probably negotiate over attributes, but for 34081ae08745Sheppo * the moment unacceptable attributes are regarded as a fatal error. 34091ae08745Sheppo * 34101ae08745Sheppo */ 34111ae08745Sheppo void 34121ae08745Sheppo vsw_process_ctrl_attr_pkt(vsw_ldc_t *ldcp, void *pkt) 34131ae08745Sheppo { 34141ae08745Sheppo vnet_attr_msg_t *attr_pkt; 34151ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 34161ae08745Sheppo vsw_port_t *port = ldcp->ldc_port; 34171ae08745Sheppo uint64_t macaddr = 0; 34181ae08745Sheppo int i; 34191ae08745Sheppo 34201ae08745Sheppo D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id); 34211ae08745Sheppo 34221ae08745Sheppo /* 34231ae08745Sheppo * We know this is a ctrl/attr packet so 34241ae08745Sheppo * cast it into the correct structure. 34251ae08745Sheppo */ 34261ae08745Sheppo attr_pkt = (vnet_attr_msg_t *)pkt; 34271ae08745Sheppo 34281ae08745Sheppo switch (attr_pkt->tag.vio_subtype) { 34291ae08745Sheppo case VIO_SUBTYPE_INFO: 34301ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 34311ae08745Sheppo 34321ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_ATTR_INFO_RECV)) 34331ae08745Sheppo return; 34341ae08745Sheppo 34351ae08745Sheppo /* 34361ae08745Sheppo * If the attributes are unacceptable then we NACK back. 34371ae08745Sheppo */ 34381ae08745Sheppo if (vsw_check_attr(attr_pkt, ldcp->ldc_port)) { 34391ae08745Sheppo 34401ae08745Sheppo DERR(vswp, "%s (chan %d): invalid attributes", 34411ae08745Sheppo __func__, ldcp->ldc_id); 34421ae08745Sheppo 34431ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 34441ae08745Sheppo 34451ae08745Sheppo attr_pkt->tag.vio_sid = ldcp->local_session; 34461ae08745Sheppo attr_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 34471ae08745Sheppo 34481ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)attr_pkt); 34491ae08745Sheppo ldcp->lane_in.lstate |= VSW_ATTR_NACK_SENT; 34501ae08745Sheppo vsw_send_msg(ldcp, (void *)attr_pkt, 34511ae08745Sheppo sizeof (vnet_attr_msg_t)); 34521ae08745Sheppo 34531ae08745Sheppo vsw_next_milestone(ldcp); 34541ae08745Sheppo return; 34551ae08745Sheppo } 34561ae08745Sheppo 34571ae08745Sheppo /* 34581ae08745Sheppo * Otherwise store attributes for this lane and update 34591ae08745Sheppo * lane state. 34601ae08745Sheppo */ 34611ae08745Sheppo ldcp->lane_in.mtu = attr_pkt->mtu; 34621ae08745Sheppo ldcp->lane_in.addr = attr_pkt->addr; 34631ae08745Sheppo ldcp->lane_in.addr_type = attr_pkt->addr_type; 34641ae08745Sheppo ldcp->lane_in.xfer_mode = attr_pkt->xfer_mode; 34651ae08745Sheppo ldcp->lane_in.ack_freq = attr_pkt->ack_freq; 34661ae08745Sheppo 34671ae08745Sheppo macaddr = ldcp->lane_in.addr; 34681ae08745Sheppo for (i = ETHERADDRL - 1; i >= 0; i--) { 34691ae08745Sheppo port->p_macaddr.ether_addr_octet[i] = macaddr & 0xFF; 34701ae08745Sheppo macaddr >>= 8; 34711ae08745Sheppo } 34721ae08745Sheppo 34731ae08745Sheppo /* create the fdb entry for this port/mac address */ 34741ae08745Sheppo (void) vsw_add_fdb(vswp, port); 34751ae08745Sheppo 34761ae08745Sheppo /* setup device specifc xmit routines */ 34771ae08745Sheppo mutex_enter(&port->tx_lock); 34781ae08745Sheppo if (ldcp->lane_in.xfer_mode == VIO_DRING_MODE) { 34791ae08745Sheppo D2(vswp, "%s: mode = VIO_DRING_MODE", __func__); 34801ae08745Sheppo port->transmit = vsw_dringsend; 34811ae08745Sheppo } else if (ldcp->lane_in.xfer_mode == VIO_DESC_MODE) { 34821ae08745Sheppo D2(vswp, "%s: mode = VIO_DESC_MODE", __func__); 34831ae08745Sheppo vsw_create_privring(ldcp); 34841ae08745Sheppo port->transmit = vsw_descrsend; 34851ae08745Sheppo } 34861ae08745Sheppo mutex_exit(&port->tx_lock); 34871ae08745Sheppo 34881ae08745Sheppo attr_pkt->tag.vio_sid = ldcp->local_session; 34891ae08745Sheppo attr_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 34901ae08745Sheppo 34911ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)attr_pkt); 34921ae08745Sheppo 34931ae08745Sheppo ldcp->lane_in.lstate |= VSW_ATTR_ACK_SENT; 34941ae08745Sheppo 34951ae08745Sheppo vsw_send_msg(ldcp, (void *)attr_pkt, 34961ae08745Sheppo sizeof (vnet_attr_msg_t)); 34971ae08745Sheppo 34981ae08745Sheppo vsw_next_milestone(ldcp); 34991ae08745Sheppo break; 35001ae08745Sheppo 35011ae08745Sheppo case VIO_SUBTYPE_ACK: 35021ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__); 35031ae08745Sheppo 35041ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_ACK_RECV)) 35051ae08745Sheppo return; 35061ae08745Sheppo 35071ae08745Sheppo ldcp->lane_out.lstate |= VSW_ATTR_ACK_RECV; 35081ae08745Sheppo vsw_next_milestone(ldcp); 35091ae08745Sheppo break; 35101ae08745Sheppo 35111ae08745Sheppo case VIO_SUBTYPE_NACK: 35121ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 35131ae08745Sheppo 35141ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_NACK_RECV)) 35151ae08745Sheppo return; 35161ae08745Sheppo 35171ae08745Sheppo ldcp->lane_out.lstate |= VSW_ATTR_NACK_RECV; 35181ae08745Sheppo vsw_next_milestone(ldcp); 35191ae08745Sheppo break; 35201ae08745Sheppo 35211ae08745Sheppo default: 35221ae08745Sheppo DERR(vswp, "%s: unknown vio_subtype %x\n", __func__, 35231ae08745Sheppo attr_pkt->tag.vio_subtype); 35241ae08745Sheppo } 35251ae08745Sheppo 35261ae08745Sheppo D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id); 35271ae08745Sheppo } 35281ae08745Sheppo 35291ae08745Sheppo /* 35301ae08745Sheppo * Process a dring info packet. We can end up here either because our peer 35311ae08745Sheppo * has ACK/NACK'ed back to an earlier DRING msg we had sent it, or our 35321ae08745Sheppo * peer has sent us a dring INFO message. 35331ae08745Sheppo * 35341ae08745Sheppo * If we get a valid/acceptable INFO packet (and we have already negotiated 35351ae08745Sheppo * a version) we ACK back and update the lane state, otherwise we NACK back. 35361ae08745Sheppo * 35371ae08745Sheppo * FUTURE: nothing to stop client from sending us info on multiple dring's 35381ae08745Sheppo * but for the moment we will just use the first one we are given. 35391ae08745Sheppo * 35401ae08745Sheppo */ 35411ae08745Sheppo void 35421ae08745Sheppo vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *ldcp, void *pkt) 35431ae08745Sheppo { 35441ae08745Sheppo vio_dring_reg_msg_t *dring_pkt; 35451ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 35461ae08745Sheppo ldc_mem_info_t minfo; 35471ae08745Sheppo dring_info_t *dp, *dbp; 35481ae08745Sheppo int dring_found = 0; 35491ae08745Sheppo 35501ae08745Sheppo /* 35511ae08745Sheppo * We know this is a ctrl/dring packet so 35521ae08745Sheppo * cast it into the correct structure. 35531ae08745Sheppo */ 35541ae08745Sheppo dring_pkt = (vio_dring_reg_msg_t *)pkt; 35551ae08745Sheppo 35561ae08745Sheppo D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id); 35571ae08745Sheppo 35581ae08745Sheppo switch (dring_pkt->tag.vio_subtype) { 35591ae08745Sheppo case VIO_SUBTYPE_INFO: 35601ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 35611ae08745Sheppo 35621ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV)) 35631ae08745Sheppo return; 35641ae08745Sheppo 35651ae08745Sheppo /* 35661ae08745Sheppo * If the dring params are unacceptable then we NACK back. 35671ae08745Sheppo */ 35681ae08745Sheppo if (vsw_check_dring_info(dring_pkt)) { 35691ae08745Sheppo 35701ae08745Sheppo DERR(vswp, "%s (%lld): invalid dring info", 35711ae08745Sheppo __func__, ldcp->ldc_id); 35721ae08745Sheppo 35731ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 35741ae08745Sheppo 35751ae08745Sheppo dring_pkt->tag.vio_sid = ldcp->local_session; 35761ae08745Sheppo dring_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 35771ae08745Sheppo 35781ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)dring_pkt); 35791ae08745Sheppo 35801ae08745Sheppo ldcp->lane_in.lstate |= VSW_DRING_NACK_SENT; 35811ae08745Sheppo 35821ae08745Sheppo vsw_send_msg(ldcp, (void *)dring_pkt, 35831ae08745Sheppo sizeof (vio_dring_reg_msg_t)); 35841ae08745Sheppo 35851ae08745Sheppo vsw_next_milestone(ldcp); 35861ae08745Sheppo return; 35871ae08745Sheppo } 35881ae08745Sheppo 35891ae08745Sheppo /* 35901ae08745Sheppo * Otherwise, attempt to map in the dring using the 35911ae08745Sheppo * cookie. If that succeeds we send back a unique dring 35921ae08745Sheppo * identifier that the sending side will use in future 35931ae08745Sheppo * to refer to this descriptor ring. 35941ae08745Sheppo */ 35951ae08745Sheppo dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP); 35961ae08745Sheppo 35971ae08745Sheppo dp->num_descriptors = dring_pkt->num_descriptors; 35981ae08745Sheppo dp->descriptor_size = dring_pkt->descriptor_size; 35991ae08745Sheppo dp->options = dring_pkt->options; 36001ae08745Sheppo dp->ncookies = dring_pkt->ncookies; 36011ae08745Sheppo 36021ae08745Sheppo /* 36031ae08745Sheppo * Note: should only get one cookie. Enforced in 36041ae08745Sheppo * the ldc layer. 36051ae08745Sheppo */ 36061ae08745Sheppo bcopy(&dring_pkt->cookie[0], &dp->cookie[0], 36071ae08745Sheppo sizeof (ldc_mem_cookie_t)); 36081ae08745Sheppo 36091ae08745Sheppo D2(vswp, "%s: num_desc %ld : desc_size %ld", __func__, 36101ae08745Sheppo dp->num_descriptors, dp->descriptor_size); 36111ae08745Sheppo D2(vswp, "%s: options 0x%lx: ncookies %ld", __func__, 36121ae08745Sheppo dp->options, dp->ncookies); 36131ae08745Sheppo 36141ae08745Sheppo if ((ldc_mem_dring_map(ldcp->ldc_handle, &dp->cookie[0], 36151ae08745Sheppo dp->ncookies, dp->num_descriptors, 36161ae08745Sheppo dp->descriptor_size, LDC_SHADOW_MAP, 36171ae08745Sheppo &(dp->handle))) != 0) { 36181ae08745Sheppo 36191ae08745Sheppo DERR(vswp, "%s: dring_map failed\n", __func__); 36201ae08745Sheppo 36211ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 36221ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 36231ae08745Sheppo 36241ae08745Sheppo dring_pkt->tag.vio_sid = ldcp->local_session; 36251ae08745Sheppo dring_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 36261ae08745Sheppo 36271ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)dring_pkt); 36281ae08745Sheppo 36291ae08745Sheppo ldcp->lane_in.lstate |= VSW_DRING_NACK_SENT; 36301ae08745Sheppo vsw_send_msg(ldcp, (void *)dring_pkt, 36311ae08745Sheppo sizeof (vio_dring_reg_msg_t)); 36321ae08745Sheppo 36331ae08745Sheppo vsw_next_milestone(ldcp); 36341ae08745Sheppo return; 36351ae08745Sheppo } 36361ae08745Sheppo 36371ae08745Sheppo if ((ldc_mem_dring_info(dp->handle, &minfo)) != 0) { 36381ae08745Sheppo 36391ae08745Sheppo DERR(vswp, "%s: dring_addr failed\n", __func__); 36401ae08745Sheppo 36411ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 36421ae08745Sheppo vsw_free_lane_resources(ldcp, INBOUND); 36431ae08745Sheppo 36441ae08745Sheppo dring_pkt->tag.vio_sid = ldcp->local_session; 36451ae08745Sheppo dring_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; 36461ae08745Sheppo 36471ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)dring_pkt); 36481ae08745Sheppo 36491ae08745Sheppo ldcp->lane_in.lstate |= VSW_DRING_NACK_SENT; 36501ae08745Sheppo vsw_send_msg(ldcp, (void *)dring_pkt, 36511ae08745Sheppo sizeof (vio_dring_reg_msg_t)); 36521ae08745Sheppo 36531ae08745Sheppo vsw_next_milestone(ldcp); 36541ae08745Sheppo return; 36551ae08745Sheppo } else { 36561ae08745Sheppo /* store the address of the pub part of ring */ 36571ae08745Sheppo dp->pub_addr = minfo.vaddr; 36581ae08745Sheppo } 36591ae08745Sheppo 36601ae08745Sheppo /* no private section as we are importing */ 36611ae08745Sheppo dp->priv_addr = NULL; 36621ae08745Sheppo 36631ae08745Sheppo /* 36641ae08745Sheppo * Using simple mono increasing int for ident at 36651ae08745Sheppo * the moment. 36661ae08745Sheppo */ 36671ae08745Sheppo dp->ident = ldcp->next_ident; 36681ae08745Sheppo ldcp->next_ident++; 36691ae08745Sheppo 36701ae08745Sheppo dp->end_idx = 0; 36711ae08745Sheppo dp->next = NULL; 36721ae08745Sheppo 36731ae08745Sheppo /* 36741ae08745Sheppo * Link it onto the end of the list of drings 36751ae08745Sheppo * for this lane. 36761ae08745Sheppo */ 36771ae08745Sheppo if (ldcp->lane_in.dringp == NULL) { 36781ae08745Sheppo D2(vswp, "%s: adding first INBOUND dring", __func__); 36791ae08745Sheppo ldcp->lane_in.dringp = dp; 36801ae08745Sheppo } else { 36811ae08745Sheppo dbp = ldcp->lane_in.dringp; 36821ae08745Sheppo 36831ae08745Sheppo while (dbp->next != NULL) 36841ae08745Sheppo dbp = dbp->next; 36851ae08745Sheppo 36861ae08745Sheppo dbp->next = dp; 36871ae08745Sheppo } 36881ae08745Sheppo 36891ae08745Sheppo /* acknowledge it */ 36901ae08745Sheppo dring_pkt->tag.vio_sid = ldcp->local_session; 36911ae08745Sheppo dring_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 36921ae08745Sheppo dring_pkt->dring_ident = dp->ident; 36931ae08745Sheppo 36941ae08745Sheppo vsw_send_msg(ldcp, (void *)dring_pkt, 36951ae08745Sheppo sizeof (vio_dring_reg_msg_t)); 36961ae08745Sheppo 36971ae08745Sheppo ldcp->lane_in.lstate |= VSW_DRING_ACK_SENT; 36981ae08745Sheppo vsw_next_milestone(ldcp); 36991ae08745Sheppo break; 37001ae08745Sheppo 37011ae08745Sheppo case VIO_SUBTYPE_ACK: 37021ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__); 37031ae08745Sheppo 37041ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_ACK_RECV)) 37051ae08745Sheppo return; 37061ae08745Sheppo 37071ae08745Sheppo /* 37081ae08745Sheppo * Peer is acknowledging our dring info and will have 37091ae08745Sheppo * sent us a dring identifier which we will use to 37101ae08745Sheppo * refer to this ring w.r.t. our peer. 37111ae08745Sheppo */ 37121ae08745Sheppo dp = ldcp->lane_out.dringp; 37131ae08745Sheppo if (dp != NULL) { 37141ae08745Sheppo /* 37151ae08745Sheppo * Find the ring this ident should be associated 37161ae08745Sheppo * with. 37171ae08745Sheppo */ 37181ae08745Sheppo if (vsw_dring_match(dp, dring_pkt)) { 37191ae08745Sheppo dring_found = 1; 37201ae08745Sheppo 37211ae08745Sheppo } else while (dp != NULL) { 37221ae08745Sheppo if (vsw_dring_match(dp, dring_pkt)) { 37231ae08745Sheppo dring_found = 1; 37241ae08745Sheppo break; 37251ae08745Sheppo } 37261ae08745Sheppo dp = dp->next; 37271ae08745Sheppo } 37281ae08745Sheppo 37291ae08745Sheppo if (dring_found == 0) { 37301ae08745Sheppo DERR(NULL, "%s: unrecognised ring cookie", 37311ae08745Sheppo __func__); 37321ae08745Sheppo vsw_restart_handshake(ldcp); 37331ae08745Sheppo return; 37341ae08745Sheppo } 37351ae08745Sheppo 37361ae08745Sheppo } else { 37371ae08745Sheppo DERR(vswp, "%s: DRING ACK received but no drings " 37381ae08745Sheppo "allocated", __func__); 37391ae08745Sheppo vsw_restart_handshake(ldcp); 37401ae08745Sheppo return; 37411ae08745Sheppo } 37421ae08745Sheppo 37431ae08745Sheppo /* store ident */ 37441ae08745Sheppo dp->ident = dring_pkt->dring_ident; 37451ae08745Sheppo ldcp->lane_out.lstate |= VSW_DRING_ACK_RECV; 37461ae08745Sheppo vsw_next_milestone(ldcp); 37471ae08745Sheppo break; 37481ae08745Sheppo 37491ae08745Sheppo case VIO_SUBTYPE_NACK: 37501ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 37511ae08745Sheppo 37521ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_NACK_RECV)) 37531ae08745Sheppo return; 37541ae08745Sheppo 37551ae08745Sheppo ldcp->lane_out.lstate |= VSW_DRING_NACK_RECV; 37561ae08745Sheppo vsw_next_milestone(ldcp); 37571ae08745Sheppo break; 37581ae08745Sheppo 37591ae08745Sheppo default: 37601ae08745Sheppo DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__, 37611ae08745Sheppo dring_pkt->tag.vio_subtype); 37621ae08745Sheppo } 37631ae08745Sheppo 37641ae08745Sheppo D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id); 37651ae08745Sheppo } 37661ae08745Sheppo 37671ae08745Sheppo /* 37681ae08745Sheppo * Process a request from peer to unregister a dring. 37691ae08745Sheppo * 37701ae08745Sheppo * For the moment we just restart the handshake if our 37711ae08745Sheppo * peer endpoint attempts to unregister a dring. 37721ae08745Sheppo */ 37731ae08745Sheppo void 37741ae08745Sheppo vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *ldcp, void *pkt) 37751ae08745Sheppo { 37761ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 37771ae08745Sheppo vio_dring_unreg_msg_t *dring_pkt; 37781ae08745Sheppo 37791ae08745Sheppo /* 37801ae08745Sheppo * We know this is a ctrl/dring packet so 37811ae08745Sheppo * cast it into the correct structure. 37821ae08745Sheppo */ 37831ae08745Sheppo dring_pkt = (vio_dring_unreg_msg_t *)pkt; 37841ae08745Sheppo 37851ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 37861ae08745Sheppo 37871ae08745Sheppo switch (dring_pkt->tag.vio_subtype) { 37881ae08745Sheppo case VIO_SUBTYPE_INFO: 37891ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 37901ae08745Sheppo 37911ae08745Sheppo DWARN(vswp, "%s: restarting handshake..", __func__); 37921ae08745Sheppo vsw_restart_handshake(ldcp); 37931ae08745Sheppo break; 37941ae08745Sheppo 37951ae08745Sheppo case VIO_SUBTYPE_ACK: 37961ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__); 37971ae08745Sheppo 37981ae08745Sheppo DWARN(vswp, "%s: restarting handshake..", __func__); 37991ae08745Sheppo vsw_restart_handshake(ldcp); 38001ae08745Sheppo break; 38011ae08745Sheppo 38021ae08745Sheppo case VIO_SUBTYPE_NACK: 38031ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 38041ae08745Sheppo 38051ae08745Sheppo DWARN(vswp, "%s: restarting handshake..", __func__); 38061ae08745Sheppo vsw_restart_handshake(ldcp); 38071ae08745Sheppo break; 38081ae08745Sheppo 38091ae08745Sheppo default: 38101ae08745Sheppo DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__, 38111ae08745Sheppo dring_pkt->tag.vio_subtype); 38121ae08745Sheppo vsw_restart_handshake(ldcp); 38131ae08745Sheppo } 38141ae08745Sheppo 38151ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 38161ae08745Sheppo } 38171ae08745Sheppo 38181ae08745Sheppo #define SND_MCST_NACK(ldcp, pkt) \ 38191ae08745Sheppo pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; \ 38201ae08745Sheppo pkt->tag.vio_sid = ldcp->local_session; \ 38211ae08745Sheppo vsw_send_msg(ldcp, (void *)pkt, sizeof (vnet_mcast_msg_t)); 38221ae08745Sheppo 38231ae08745Sheppo /* 38241ae08745Sheppo * Process a multicast request from a vnet. 38251ae08745Sheppo * 38261ae08745Sheppo * Vnet's specify a multicast address that they are interested in. This 38271ae08745Sheppo * address is used as a key into the hash table which forms the multicast 38281ae08745Sheppo * forwarding database (mFDB). 38291ae08745Sheppo * 38301ae08745Sheppo * The table keys are the multicast addresses, while the table entries 38311ae08745Sheppo * are pointers to lists of ports which wish to receive packets for the 38321ae08745Sheppo * specified multicast address. 38331ae08745Sheppo * 38341ae08745Sheppo * When a multicast packet is being switched we use the address as a key 38351ae08745Sheppo * into the hash table, and then walk the appropriate port list forwarding 38361ae08745Sheppo * the pkt to each port in turn. 38371ae08745Sheppo * 38381ae08745Sheppo * If a vnet is no longer interested in a particular multicast grouping 38391ae08745Sheppo * we simply find the correct location in the hash table and then delete 38401ae08745Sheppo * the relevant port from the port list. 38411ae08745Sheppo * 38421ae08745Sheppo * To deal with the case whereby a port is being deleted without first 38431ae08745Sheppo * removing itself from the lists in the hash table, we maintain a list 38441ae08745Sheppo * of multicast addresses the port has registered an interest in, within 38451ae08745Sheppo * the port structure itself. We then simply walk that list of addresses 38461ae08745Sheppo * using them as keys into the hash table and remove the port from the 38471ae08745Sheppo * appropriate lists. 38481ae08745Sheppo */ 38491ae08745Sheppo static void 38501ae08745Sheppo vsw_process_ctrl_mcst_pkt(vsw_ldc_t *ldcp, void *pkt) 38511ae08745Sheppo { 38521ae08745Sheppo vnet_mcast_msg_t *mcst_pkt; 38531ae08745Sheppo vsw_port_t *port = ldcp->ldc_port; 38541ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 38551ae08745Sheppo int i; 38561ae08745Sheppo 38571ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 38581ae08745Sheppo 38591ae08745Sheppo /* 38601ae08745Sheppo * We know this is a ctrl/mcast packet so 38611ae08745Sheppo * cast it into the correct structure. 38621ae08745Sheppo */ 38631ae08745Sheppo mcst_pkt = (vnet_mcast_msg_t *)pkt; 38641ae08745Sheppo 38651ae08745Sheppo switch (mcst_pkt->tag.vio_subtype) { 38661ae08745Sheppo case VIO_SUBTYPE_INFO: 38671ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 38681ae08745Sheppo 38691ae08745Sheppo /* 38701ae08745Sheppo * Check if in correct state to receive a multicast 38711ae08745Sheppo * message (i.e. handshake complete). If not reset 38721ae08745Sheppo * the handshake. 38731ae08745Sheppo */ 38741ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_MCST_INFO_RECV)) 38751ae08745Sheppo return; 38761ae08745Sheppo 38771ae08745Sheppo /* 38781ae08745Sheppo * Before attempting to add or remove address check 38791ae08745Sheppo * that they are valid multicast addresses. 38801ae08745Sheppo * If not, then NACK back. 38811ae08745Sheppo */ 38821ae08745Sheppo for (i = 0; i < mcst_pkt->count; i++) { 38831ae08745Sheppo if ((mcst_pkt->mca[i].ether_addr_octet[0] & 01) != 1) { 38841ae08745Sheppo DERR(vswp, "%s: invalid multicast address", 38851ae08745Sheppo __func__); 38861ae08745Sheppo SND_MCST_NACK(ldcp, mcst_pkt); 38871ae08745Sheppo return; 38881ae08745Sheppo } 38891ae08745Sheppo } 38901ae08745Sheppo 38911ae08745Sheppo /* 38921ae08745Sheppo * Now add/remove the addresses. If this fails we 38931ae08745Sheppo * NACK back. 38941ae08745Sheppo */ 38951ae08745Sheppo if (vsw_add_rem_mcst(mcst_pkt, port) != 0) { 38961ae08745Sheppo SND_MCST_NACK(ldcp, mcst_pkt); 38971ae08745Sheppo return; 38981ae08745Sheppo } 38991ae08745Sheppo 39001ae08745Sheppo mcst_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 39011ae08745Sheppo mcst_pkt->tag.vio_sid = ldcp->local_session; 39021ae08745Sheppo 39031ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)mcst_pkt); 39041ae08745Sheppo 39051ae08745Sheppo vsw_send_msg(ldcp, (void *)mcst_pkt, 39061ae08745Sheppo sizeof (vnet_mcast_msg_t)); 39071ae08745Sheppo break; 39081ae08745Sheppo 39091ae08745Sheppo case VIO_SUBTYPE_ACK: 39101ae08745Sheppo DWARN(vswp, "%s: VIO_SUBTYPE_ACK", __func__); 39111ae08745Sheppo 39121ae08745Sheppo /* 39131ae08745Sheppo * We shouldn't ever get a multicast ACK message as 39141ae08745Sheppo * at the moment we never request multicast addresses 39151ae08745Sheppo * to be set on some other device. This may change in 39161ae08745Sheppo * the future if we have cascading switches. 39171ae08745Sheppo */ 39181ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_ACK_RECV)) 39191ae08745Sheppo return; 39201ae08745Sheppo 39211ae08745Sheppo /* Do nothing */ 39221ae08745Sheppo break; 39231ae08745Sheppo 39241ae08745Sheppo case VIO_SUBTYPE_NACK: 39251ae08745Sheppo DWARN(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 39261ae08745Sheppo 39271ae08745Sheppo /* 39281ae08745Sheppo * We shouldn't get a multicast NACK packet for the 39291ae08745Sheppo * same reasons as we shouldn't get a ACK packet. 39301ae08745Sheppo */ 39311ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_NACK_RECV)) 39321ae08745Sheppo return; 39331ae08745Sheppo 39341ae08745Sheppo /* Do nothing */ 39351ae08745Sheppo break; 39361ae08745Sheppo 39371ae08745Sheppo default: 39381ae08745Sheppo DERR(vswp, "%s: unknown vio_subtype %x\n", __func__, 39391ae08745Sheppo mcst_pkt->tag.vio_subtype); 39401ae08745Sheppo } 39411ae08745Sheppo 39421ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 39431ae08745Sheppo } 39441ae08745Sheppo 39451ae08745Sheppo static void 39461ae08745Sheppo vsw_process_ctrl_rdx_pkt(vsw_ldc_t *ldcp, void *pkt) 39471ae08745Sheppo { 39481ae08745Sheppo vio_rdx_msg_t *rdx_pkt; 39491ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 39501ae08745Sheppo 39511ae08745Sheppo /* 39521ae08745Sheppo * We know this is a ctrl/rdx packet so 39531ae08745Sheppo * cast it into the correct structure. 39541ae08745Sheppo */ 39551ae08745Sheppo rdx_pkt = (vio_rdx_msg_t *)pkt; 39561ae08745Sheppo 39571ae08745Sheppo D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id); 39581ae08745Sheppo 39591ae08745Sheppo switch (rdx_pkt->tag.vio_subtype) { 39601ae08745Sheppo case VIO_SUBTYPE_INFO: 39611ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 39621ae08745Sheppo 39631ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_RDX_INFO_RECV)) 39641ae08745Sheppo return; 39651ae08745Sheppo 39661ae08745Sheppo rdx_pkt->tag.vio_sid = ldcp->local_session; 39671ae08745Sheppo rdx_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 39681ae08745Sheppo 39691ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)rdx_pkt); 39701ae08745Sheppo 39711ae08745Sheppo ldcp->lane_in.lstate |= VSW_RDX_ACK_SENT; 39721ae08745Sheppo 39731ae08745Sheppo vsw_send_msg(ldcp, (void *)rdx_pkt, 39741ae08745Sheppo sizeof (vio_rdx_msg_t)); 39751ae08745Sheppo 39761ae08745Sheppo vsw_next_milestone(ldcp); 39771ae08745Sheppo break; 39781ae08745Sheppo 39791ae08745Sheppo case VIO_SUBTYPE_ACK: 39801ae08745Sheppo /* 39811ae08745Sheppo * Should be handled in-band by callback handler. 39821ae08745Sheppo */ 39831ae08745Sheppo DERR(vswp, "%s: Unexpected VIO_SUBTYPE_ACK", __func__); 39841ae08745Sheppo vsw_restart_handshake(ldcp); 39851ae08745Sheppo break; 39861ae08745Sheppo 39871ae08745Sheppo case VIO_SUBTYPE_NACK: 39881ae08745Sheppo D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 39891ae08745Sheppo 39901ae08745Sheppo if (vsw_check_flag(ldcp, OUTBOUND, VSW_RDX_NACK_RECV)) 39911ae08745Sheppo return; 39921ae08745Sheppo 39931ae08745Sheppo ldcp->lane_out.lstate |= VSW_RDX_NACK_RECV; 39941ae08745Sheppo vsw_next_milestone(ldcp); 39951ae08745Sheppo break; 39961ae08745Sheppo 39971ae08745Sheppo default: 39981ae08745Sheppo DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__, 39991ae08745Sheppo rdx_pkt->tag.vio_subtype); 40001ae08745Sheppo } 40011ae08745Sheppo 40021ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 40031ae08745Sheppo } 40041ae08745Sheppo 40051ae08745Sheppo static void 40061ae08745Sheppo vsw_process_data_pkt(vsw_ldc_t *ldcp, void *dpkt, vio_msg_tag_t tag) 40071ae08745Sheppo { 40081ae08745Sheppo uint16_t env = tag.vio_subtype_env; 40091ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 40101ae08745Sheppo 40111ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 40121ae08745Sheppo 40131ae08745Sheppo /* session id check */ 40141ae08745Sheppo if (ldcp->session_status & VSW_PEER_SESSION) { 40151ae08745Sheppo if (ldcp->peer_session != tag.vio_sid) { 40161ae08745Sheppo DERR(vswp, "%s (chan %d): invalid session id (%llx)", 40171ae08745Sheppo __func__, ldcp->ldc_id, tag.vio_sid); 40181ae08745Sheppo vsw_restart_handshake(ldcp); 40191ae08745Sheppo return; 40201ae08745Sheppo } 40211ae08745Sheppo } 40221ae08745Sheppo 40231ae08745Sheppo /* 40241ae08745Sheppo * It is an error for us to be getting data packets 40251ae08745Sheppo * before the handshake has completed. 40261ae08745Sheppo */ 40271ae08745Sheppo if (ldcp->hphase != VSW_MILESTONE4) { 40281ae08745Sheppo DERR(vswp, "%s: got data packet before handshake complete " 40291ae08745Sheppo "hphase %d (%x: %x)", __func__, ldcp->hphase, 40301ae08745Sheppo ldcp->lane_in.lstate, ldcp->lane_out.lstate); 40311ae08745Sheppo DUMP_FLAGS(ldcp->lane_in.lstate); 40321ae08745Sheppo DUMP_FLAGS(ldcp->lane_out.lstate); 40331ae08745Sheppo vsw_restart_handshake(ldcp); 40341ae08745Sheppo return; 40351ae08745Sheppo } 40361ae08745Sheppo 40371ae08745Sheppo /* 40381ae08745Sheppo * Switch on vio_subtype envelope, then let lower routines 40391ae08745Sheppo * decide if its an INFO, ACK or NACK packet. 40401ae08745Sheppo */ 40411ae08745Sheppo if (env == VIO_DRING_DATA) { 40421ae08745Sheppo vsw_process_data_dring_pkt(ldcp, dpkt); 40431ae08745Sheppo } else if (env == VIO_PKT_DATA) { 40441ae08745Sheppo vsw_process_data_raw_pkt(ldcp, dpkt); 40451ae08745Sheppo } else if (env == VIO_DESC_DATA) { 40461ae08745Sheppo vsw_process_data_ibnd_pkt(ldcp, dpkt); 40471ae08745Sheppo } else { 40481ae08745Sheppo DERR(vswp, "%s : unknown vio_subtype_env (%x)\n", 40491ae08745Sheppo __func__, env); 40501ae08745Sheppo } 40511ae08745Sheppo 40521ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 40531ae08745Sheppo } 40541ae08745Sheppo 40551ae08745Sheppo #define SND_DRING_NACK(ldcp, pkt) \ 40561ae08745Sheppo pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; \ 40571ae08745Sheppo pkt->tag.vio_sid = ldcp->local_session; \ 40581ae08745Sheppo vsw_send_msg(ldcp, (void *)pkt, sizeof (vio_dring_msg_t)); 40591ae08745Sheppo 40601ae08745Sheppo static void 40611ae08745Sheppo vsw_process_data_dring_pkt(vsw_ldc_t *ldcp, void *dpkt) 40621ae08745Sheppo { 40631ae08745Sheppo vio_dring_msg_t *dring_pkt; 40641ae08745Sheppo vnet_public_desc_t *pub_addr = NULL; 40651ae08745Sheppo vsw_private_desc_t *priv_addr = NULL; 40661ae08745Sheppo dring_info_t *dp = NULL; 40671ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 40681ae08745Sheppo mblk_t *mp = NULL; 40691ae08745Sheppo mblk_t *bp = NULL; 40701ae08745Sheppo mblk_t *bpt = NULL; 40711ae08745Sheppo size_t nbytes = 0; 40721ae08745Sheppo size_t off = 0; 40731ae08745Sheppo uint64_t ncookies = 0; 40741ae08745Sheppo uint64_t chain = 0; 40751ae08745Sheppo uint64_t j, len, num; 40761ae08745Sheppo uint32_t start, end, datalen; 40771ae08745Sheppo int i, last_sync, rv; 40781ae08745Sheppo boolean_t ack_needed = B_FALSE; 40791ae08745Sheppo boolean_t sync_needed = B_TRUE; 40801ae08745Sheppo 40811ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 40821ae08745Sheppo 40831ae08745Sheppo /* 40841ae08745Sheppo * We know this is a data/dring packet so 40851ae08745Sheppo * cast it into the correct structure. 40861ae08745Sheppo */ 40871ae08745Sheppo dring_pkt = (vio_dring_msg_t *)dpkt; 40881ae08745Sheppo 40891ae08745Sheppo /* 40901ae08745Sheppo * Switch on the vio_subtype. If its INFO then we need to 40911ae08745Sheppo * process the data. If its an ACK we need to make sure 40921ae08745Sheppo * it makes sense (i.e did we send an earlier data/info), 40931ae08745Sheppo * and if its a NACK then we maybe attempt a retry. 40941ae08745Sheppo */ 40951ae08745Sheppo switch (dring_pkt->tag.vio_subtype) { 40961ae08745Sheppo case VIO_SUBTYPE_INFO: 40971ae08745Sheppo D2(vswp, "%s(%lld): VIO_SUBTYPE_INFO", __func__, ldcp->ldc_id); 40981ae08745Sheppo 40991ae08745Sheppo if ((dp = vsw_ident2dring(&ldcp->lane_in, 41001ae08745Sheppo dring_pkt->dring_ident)) == NULL) { 41011ae08745Sheppo 41021ae08745Sheppo DERR(vswp, "%s(%lld): unable to find dring from " 41031ae08745Sheppo "ident 0x%llx", __func__, ldcp->ldc_id, 41041ae08745Sheppo dring_pkt->dring_ident); 41051ae08745Sheppo 41061ae08745Sheppo SND_DRING_NACK(ldcp, dring_pkt); 41071ae08745Sheppo return; 41081ae08745Sheppo } 41091ae08745Sheppo 41101ae08745Sheppo start = end = 0; 41111ae08745Sheppo start = dring_pkt->start_idx; 41121ae08745Sheppo end = dring_pkt->end_idx; 41131ae08745Sheppo 41141ae08745Sheppo D3(vswp, "%s(%lld): start index %ld : end %ld\n", 41151ae08745Sheppo __func__, ldcp->ldc_id, start, end); 41161ae08745Sheppo 41171ae08745Sheppo /* basic sanity check */ 41181ae08745Sheppo len = dp->num_descriptors; 41191ae08745Sheppo if (end > len) { 41201ae08745Sheppo DERR(vswp, "%s(%lld): endpoint %lld outside ring" 41211ae08745Sheppo " length %lld", __func__, ldcp->ldc_id, 41221ae08745Sheppo end, len); 41231ae08745Sheppo 41241ae08745Sheppo SND_DRING_NACK(ldcp, dring_pkt); 41251ae08745Sheppo return; 41261ae08745Sheppo } 41271ae08745Sheppo 41281ae08745Sheppo /* sync data */ 41291ae08745Sheppo if ((rv = ldc_mem_dring_acquire(dp->handle, 41301ae08745Sheppo start, end)) != 0) { 41311ae08745Sheppo DERR(vswp, "%s(%lld): unable to acquire dring : err %d", 41321ae08745Sheppo __func__, ldcp->ldc_id, rv); 41331ae08745Sheppo return; 41341ae08745Sheppo } 41351ae08745Sheppo 41361ae08745Sheppo pub_addr = (vnet_public_desc_t *)dp->pub_addr; 41371ae08745Sheppo 41381ae08745Sheppo j = num = 0; 41391ae08745Sheppo 41401ae08745Sheppo /* calculate # descriptors taking into a/c wrap around */ 41411ae08745Sheppo num = end >= start ? end - start + 1: (len - start + 1) + end; 41421ae08745Sheppo 41431ae08745Sheppo last_sync = start; 41441ae08745Sheppo 41451ae08745Sheppo for (i = start; j < num; i = (i + 1) % len, j++) { 41461ae08745Sheppo pub_addr = (vnet_public_desc_t *)dp->pub_addr + i; 41471ae08745Sheppo 41481ae08745Sheppo /* 41491ae08745Sheppo * Data is padded to align on 8 byte boundary, 41501ae08745Sheppo * datalen is actual data length, i.e. minus that 41511ae08745Sheppo * padding. 41521ae08745Sheppo */ 41531ae08745Sheppo datalen = pub_addr->nbytes; 41541ae08745Sheppo 41551ae08745Sheppo /* 41561ae08745Sheppo * Does peer wish us to ACK when we have finished 41571ae08745Sheppo * with this descriptor ? 41581ae08745Sheppo */ 41591ae08745Sheppo if (pub_addr->hdr.ack) 41601ae08745Sheppo ack_needed = B_TRUE; 41611ae08745Sheppo 41621ae08745Sheppo D2(vswp, "%s(%lld): processing desc %lld at pos" 41631ae08745Sheppo " 0x%llx : dstate 0x%lx : datalen 0x%lx", 41641ae08745Sheppo __func__, ldcp->ldc_id, i, pub_addr, 41651ae08745Sheppo pub_addr->hdr.dstate, datalen); 41661ae08745Sheppo 41671ae08745Sheppo /* 41681ae08745Sheppo * XXXX : Is it a fatal error to be told to 41691ae08745Sheppo * process a packet when the READY bit is not 41701ae08745Sheppo * set ? 41711ae08745Sheppo */ 41721ae08745Sheppo if (pub_addr->hdr.dstate != VIO_DESC_READY) { 41731ae08745Sheppo DERR(vswp, "%s(%d): descriptor %lld at pos " 41741ae08745Sheppo " 0x%llx not READY (0x%lx)", __func__, 41751ae08745Sheppo ldcp->ldc_id, i, pub_addr, 41761ae08745Sheppo pub_addr->hdr.dstate); 41771ae08745Sheppo 41781ae08745Sheppo SND_DRING_NACK(ldcp, dring_pkt); 41791ae08745Sheppo (void) ldc_mem_dring_release(dp->handle, 41801ae08745Sheppo start, end); 41811ae08745Sheppo return; 41821ae08745Sheppo } 41831ae08745Sheppo 41841ae08745Sheppo /* 41851ae08745Sheppo * Mark that we are starting to process descriptor. 41861ae08745Sheppo */ 41871ae08745Sheppo pub_addr->hdr.dstate = VIO_DESC_ACCEPTED; 41881ae08745Sheppo 41891ae08745Sheppo /* 41901ae08745Sheppo * allocb(9F) returns an aligned data block. We 41911ae08745Sheppo * need to ensure that we ask ldc for an aligned 41921ae08745Sheppo * number of bytes also. 41931ae08745Sheppo */ 41941ae08745Sheppo nbytes = datalen; 41951ae08745Sheppo if (nbytes & 0x7) { 41961ae08745Sheppo off = 8 - (nbytes & 0x7); 41971ae08745Sheppo nbytes += off; 41981ae08745Sheppo } 41991ae08745Sheppo mp = allocb(datalen, BPRI_MED); 42001ae08745Sheppo if (mp == NULL) { 42011ae08745Sheppo DERR(vswp, "%s(%lld): allocb failed", 42021ae08745Sheppo __func__, ldcp->ldc_id); 42031ae08745Sheppo (void) ldc_mem_dring_release(dp->handle, 42041ae08745Sheppo start, end); 42051ae08745Sheppo return; 42061ae08745Sheppo } 42071ae08745Sheppo 42081ae08745Sheppo ncookies = pub_addr->ncookies; 42091ae08745Sheppo rv = ldc_mem_copy(ldcp->ldc_handle, 42101ae08745Sheppo (caddr_t)mp->b_rptr, 0, &nbytes, 42111ae08745Sheppo pub_addr->memcookie, ncookies, 42121ae08745Sheppo LDC_COPY_IN); 42131ae08745Sheppo 42141ae08745Sheppo if (rv != 0) { 42151ae08745Sheppo DERR(vswp, "%s(%d): unable to copy in " 42161ae08745Sheppo "data from %d cookies", __func__, 42171ae08745Sheppo ldcp->ldc_id, ncookies); 42181ae08745Sheppo freemsg(mp); 42191ae08745Sheppo (void) ldc_mem_dring_release(dp->handle, 42201ae08745Sheppo start, end); 42211ae08745Sheppo return; 42221ae08745Sheppo } else { 42231ae08745Sheppo D2(vswp, "%s(%d): copied in %ld bytes" 42241ae08745Sheppo " using %d cookies", __func__, 42251ae08745Sheppo ldcp->ldc_id, nbytes, ncookies); 42261ae08745Sheppo } 42271ae08745Sheppo 42281ae08745Sheppo /* point to the actual end of data */ 42291ae08745Sheppo mp->b_wptr = mp->b_rptr + datalen; 42301ae08745Sheppo 42311ae08745Sheppo /* build a chain of received packets */ 42321ae08745Sheppo if (bp == NULL) { 42331ae08745Sheppo /* first pkt */ 42341ae08745Sheppo bp = mp; 42351ae08745Sheppo bp->b_next = bp->b_prev = NULL; 42361ae08745Sheppo bpt = bp; 42371ae08745Sheppo chain = 1; 42381ae08745Sheppo } else { 42391ae08745Sheppo mp->b_next = NULL; 42401ae08745Sheppo mp->b_prev = bpt; 42411ae08745Sheppo bpt->b_next = mp; 42421ae08745Sheppo bpt = mp; 42431ae08745Sheppo chain++; 42441ae08745Sheppo } 42451ae08745Sheppo 42461ae08745Sheppo /* mark we are finished with this descriptor */ 42471ae08745Sheppo pub_addr->hdr.dstate = VIO_DESC_DONE; 42481ae08745Sheppo 42491ae08745Sheppo /* 42501ae08745Sheppo * Send an ACK back to peer if requested, and sync 42511ae08745Sheppo * the rings up to this point so the remote side sees 42521ae08745Sheppo * the descriptor flag in a consistent state. 42531ae08745Sheppo */ 42541ae08745Sheppo if (ack_needed) { 42551ae08745Sheppo if ((rv = ldc_mem_dring_release( 42561ae08745Sheppo dp->handle, last_sync, i)) != 0) { 42571ae08745Sheppo DERR(vswp, "%s(%lld): unable to sync" 42581ae08745Sheppo " from %d to %d", __func__, 42591ae08745Sheppo ldcp->ldc_id, last_sync, i); 42601ae08745Sheppo } 42611ae08745Sheppo 42621ae08745Sheppo ack_needed = B_FALSE; 42631ae08745Sheppo 42641ae08745Sheppo if (i == end) 42651ae08745Sheppo sync_needed = B_FALSE; 42661ae08745Sheppo else 42671ae08745Sheppo sync_needed = B_TRUE; 42681ae08745Sheppo 42691ae08745Sheppo last_sync = (i + 1) % len; 42701ae08745Sheppo 42711ae08745Sheppo dring_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK; 42721ae08745Sheppo dring_pkt->tag.vio_sid = ldcp->local_session; 42731ae08745Sheppo vsw_send_msg(ldcp, (void *)dring_pkt, 42741ae08745Sheppo sizeof (vio_dring_msg_t)); 42751ae08745Sheppo } 42761ae08745Sheppo } 42771ae08745Sheppo 42781ae08745Sheppo if (sync_needed) { 42791ae08745Sheppo if ((rv = ldc_mem_dring_release(dp->handle, 42801ae08745Sheppo last_sync, end)) != 0) { 42811ae08745Sheppo DERR(vswp, "%s(%lld): unable to sync" 42821ae08745Sheppo " from %d to %d", __func__, 42831ae08745Sheppo ldcp->ldc_id, last_sync, end); 42841ae08745Sheppo } 42851ae08745Sheppo } 42861ae08745Sheppo 42871ae08745Sheppo /* send the chain of packets to be switched */ 42881ae08745Sheppo D3(vswp, "%s(%lld): switching chain of %d msgs", __func__, 42891ae08745Sheppo ldcp->ldc_id, chain); 42901ae08745Sheppo vsw_switch_frame(vswp, bp, VSW_VNETPORT, 42911ae08745Sheppo ldcp->ldc_port, NULL); 42921ae08745Sheppo 42931ae08745Sheppo break; 42941ae08745Sheppo 42951ae08745Sheppo case VIO_SUBTYPE_ACK: 42961ae08745Sheppo D2(vswp, "%s(%lld): VIO_SUBTYPE_ACK", __func__, ldcp->ldc_id); 42971ae08745Sheppo /* 42981ae08745Sheppo * Verify that the relevant descriptors are all 42991ae08745Sheppo * marked as DONE 43001ae08745Sheppo */ 43011ae08745Sheppo if ((dp = vsw_ident2dring(&ldcp->lane_out, 43021ae08745Sheppo dring_pkt->dring_ident)) == NULL) { 43031ae08745Sheppo DERR(vswp, "%s: unknown ident in ACK", __func__); 43041ae08745Sheppo return; 43051ae08745Sheppo } 43061ae08745Sheppo 43071ae08745Sheppo pub_addr = (vnet_public_desc_t *)dp->pub_addr; 43081ae08745Sheppo priv_addr = (vsw_private_desc_t *)dp->priv_addr; 43091ae08745Sheppo 43101ae08745Sheppo start = end = 0; 43111ae08745Sheppo start = dring_pkt->start_idx; 43121ae08745Sheppo end = dring_pkt->end_idx; 43131ae08745Sheppo len = dp->num_descriptors; 43141ae08745Sheppo 43151ae08745Sheppo 43161ae08745Sheppo j = num = 0; 43171ae08745Sheppo /* calculate # descriptors taking into a/c wrap around */ 43181ae08745Sheppo num = end >= start ? end - start + 1: (len - start + 1) + end; 43191ae08745Sheppo 43201ae08745Sheppo D2(vswp, "%s(%lld): start index %ld : end %ld : num %ld\n", 43211ae08745Sheppo __func__, ldcp->ldc_id, start, end, num); 43221ae08745Sheppo 43231ae08745Sheppo for (i = start; j < num; i = (i + 1) % len, j++) { 43241ae08745Sheppo pub_addr = (vnet_public_desc_t *)dp->pub_addr + i; 43251ae08745Sheppo priv_addr = (vsw_private_desc_t *)dp->priv_addr + i; 43261ae08745Sheppo 43271ae08745Sheppo if (pub_addr->hdr.dstate != VIO_DESC_DONE) { 43281ae08745Sheppo DERR(vswp, "%s: descriptor %lld at pos " 43291ae08745Sheppo " 0x%llx not DONE (0x%lx)\n", __func__, 43301ae08745Sheppo i, pub_addr, pub_addr->hdr.dstate); 43311ae08745Sheppo return; 43321ae08745Sheppo } else { 43331ae08745Sheppo /* clear all the fields */ 43341ae08745Sheppo bzero(priv_addr->datap, priv_addr->datalen); 43351ae08745Sheppo priv_addr->datalen = 0; 43361ae08745Sheppo 43371ae08745Sheppo pub_addr->hdr.dstate = VIO_DESC_FREE; 43381ae08745Sheppo pub_addr->hdr.ack = 0; 43391ae08745Sheppo priv_addr->dstate = VIO_DESC_FREE; 43401ae08745Sheppo 43411ae08745Sheppo D3(vswp, "clearing descp %d : pub state " 43421ae08745Sheppo "0x%llx : priv state 0x%llx", i, 43431ae08745Sheppo pub_addr->hdr.dstate, 43441ae08745Sheppo priv_addr->dstate); 43451ae08745Sheppo } 43461ae08745Sheppo } 43471ae08745Sheppo 43481ae08745Sheppo break; 43491ae08745Sheppo 43501ae08745Sheppo case VIO_SUBTYPE_NACK: 43511ae08745Sheppo DWARN(vswp, "%s(%lld): VIO_SUBTYPE_NACK", 43521ae08745Sheppo __func__, ldcp->ldc_id); 43531ae08745Sheppo /* 43541ae08745Sheppo * Something is badly wrong if we are getting NACK's 43551ae08745Sheppo * for our data pkts. So reset the channel. 43561ae08745Sheppo */ 43571ae08745Sheppo vsw_restart_handshake(ldcp); 43581ae08745Sheppo 43591ae08745Sheppo break; 43601ae08745Sheppo 43611ae08745Sheppo default: 43621ae08745Sheppo DERR(vswp, "%s(%lld): Unknown vio_subtype %x\n", __func__, 43631ae08745Sheppo ldcp->ldc_id, dring_pkt->tag.vio_subtype); 43641ae08745Sheppo } 43651ae08745Sheppo 43661ae08745Sheppo D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id); 43671ae08745Sheppo } 43681ae08745Sheppo 43691ae08745Sheppo /* 43701ae08745Sheppo * VIO_PKT_DATA (a.k.a raw data mode ) 43711ae08745Sheppo * 43721ae08745Sheppo * Note - currently not supported. Do nothing. 43731ae08745Sheppo */ 43741ae08745Sheppo static void 43751ae08745Sheppo vsw_process_data_raw_pkt(vsw_ldc_t *ldcp, void *dpkt) 43761ae08745Sheppo { 43771ae08745Sheppo _NOTE(ARGUNUSED(dpkt)) 43781ae08745Sheppo 43791ae08745Sheppo D1(NULL, "%s (%lld): enter\n", __func__, ldcp->ldc_id); 43801ae08745Sheppo 43811ae08745Sheppo DERR(NULL, "%s (%lld): currently not supported", 43821ae08745Sheppo __func__, ldcp->ldc_id); 43831ae08745Sheppo 43841ae08745Sheppo D1(NULL, "%s (%lld): exit\n", __func__, ldcp->ldc_id); 43851ae08745Sheppo } 43861ae08745Sheppo 43871ae08745Sheppo #define SND_IBND_DESC_NACK(ldcp, pkt) \ 43881ae08745Sheppo pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; \ 43891ae08745Sheppo pkt->tag.vio_sid = ldcp->local_session; \ 43901ae08745Sheppo vsw_send_msg(ldcp, (void *)pkt, sizeof (vio_ibnd_desc_t)); 43911ae08745Sheppo 43921ae08745Sheppo /* 43931ae08745Sheppo * Process an in-band descriptor message (most likely from 43941ae08745Sheppo * OBP). 43951ae08745Sheppo */ 43961ae08745Sheppo static void 43971ae08745Sheppo vsw_process_data_ibnd_pkt(vsw_ldc_t *ldcp, void *pkt) 43981ae08745Sheppo { 43991ae08745Sheppo vio_ibnd_desc_t *ibnd_desc; 44001ae08745Sheppo dring_info_t *dp = NULL; 44011ae08745Sheppo vsw_private_desc_t *priv_addr = NULL; 44021ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 44031ae08745Sheppo mblk_t *mp = NULL; 44041ae08745Sheppo size_t nbytes = 0; 44051ae08745Sheppo size_t off = 0; 44061ae08745Sheppo uint64_t idx = 0; 44071ae08745Sheppo uint32_t datalen = 0; 44081ae08745Sheppo uint64_t ncookies = 0; 44091ae08745Sheppo int rv; 44101ae08745Sheppo 44111ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 44121ae08745Sheppo 44131ae08745Sheppo ibnd_desc = (vio_ibnd_desc_t *)pkt; 44141ae08745Sheppo 44151ae08745Sheppo switch (ibnd_desc->hdr.tag.vio_subtype) { 44161ae08745Sheppo case VIO_SUBTYPE_INFO: 44171ae08745Sheppo D1(vswp, "%s: VIO_SUBTYPE_INFO", __func__); 44181ae08745Sheppo 44191ae08745Sheppo if (vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV)) 44201ae08745Sheppo return; 44211ae08745Sheppo 44221ae08745Sheppo /* 44231ae08745Sheppo * Data is padded to align on a 8 byte boundary, 44241ae08745Sheppo * nbytes is actual data length, i.e. minus that 44251ae08745Sheppo * padding. 44261ae08745Sheppo */ 44271ae08745Sheppo datalen = ibnd_desc->nbytes; 44281ae08745Sheppo 44291ae08745Sheppo D2(vswp, "%s(%lld): processing inband desc : " 44301ae08745Sheppo ": datalen 0x%lx", __func__, ldcp->ldc_id, datalen); 44311ae08745Sheppo 44321ae08745Sheppo ncookies = ibnd_desc->ncookies; 44331ae08745Sheppo 44341ae08745Sheppo /* 44351ae08745Sheppo * allocb(9F) returns an aligned data block. We 44361ae08745Sheppo * need to ensure that we ask ldc for an aligned 44371ae08745Sheppo * number of bytes also. 44381ae08745Sheppo */ 44391ae08745Sheppo nbytes = datalen; 44401ae08745Sheppo if (nbytes & 0x7) { 44411ae08745Sheppo off = 8 - (nbytes & 0x7); 44421ae08745Sheppo nbytes += off; 44431ae08745Sheppo } 44441ae08745Sheppo 44451ae08745Sheppo mp = allocb(datalen, BPRI_MED); 44461ae08745Sheppo if (mp == NULL) { 44471ae08745Sheppo DERR(vswp, "%s(%lld): allocb failed", 44481ae08745Sheppo __func__, ldcp->ldc_id); 44491ae08745Sheppo return; 44501ae08745Sheppo } 44511ae08745Sheppo 44521ae08745Sheppo rv = ldc_mem_copy(ldcp->ldc_handle, (caddr_t)mp->b_rptr, 44531ae08745Sheppo 0, &nbytes, ibnd_desc->memcookie, (uint64_t)ncookies, 44541ae08745Sheppo LDC_COPY_IN); 44551ae08745Sheppo 44561ae08745Sheppo if (rv != 0) { 44571ae08745Sheppo DERR(vswp, "%s(%d): unable to copy in data from " 44581ae08745Sheppo "%d cookie(s)", __func__, 44591ae08745Sheppo ldcp->ldc_id, ncookies); 44601ae08745Sheppo freemsg(mp); 44611ae08745Sheppo return; 44621ae08745Sheppo } else { 44631ae08745Sheppo D2(vswp, "%s(%d): copied in %ld bytes using %d " 44641ae08745Sheppo "cookies", __func__, ldcp->ldc_id, nbytes, 44651ae08745Sheppo ncookies); 44661ae08745Sheppo } 44671ae08745Sheppo 44681ae08745Sheppo /* point to the actual end of data */ 44691ae08745Sheppo mp->b_wptr = mp->b_rptr + datalen; 44701ae08745Sheppo 44711ae08745Sheppo /* 44721ae08745Sheppo * We ACK back every in-band descriptor message we process 44731ae08745Sheppo */ 44741ae08745Sheppo ibnd_desc->hdr.tag.vio_subtype = VIO_SUBTYPE_ACK; 44751ae08745Sheppo ibnd_desc->hdr.tag.vio_sid = ldcp->local_session; 44761ae08745Sheppo vsw_send_msg(ldcp, (void *)ibnd_desc, 44771ae08745Sheppo sizeof (vio_ibnd_desc_t)); 44781ae08745Sheppo 44791ae08745Sheppo /* send the packet to be switched */ 44801ae08745Sheppo vsw_switch_frame(vswp, mp, VSW_VNETPORT, 44811ae08745Sheppo ldcp->ldc_port, NULL); 44821ae08745Sheppo 44831ae08745Sheppo break; 44841ae08745Sheppo 44851ae08745Sheppo case VIO_SUBTYPE_ACK: 44861ae08745Sheppo D1(vswp, "%s: VIO_SUBTYPE_ACK", __func__); 44871ae08745Sheppo 44881ae08745Sheppo /* Verify the ACK is valid */ 44891ae08745Sheppo idx = ibnd_desc->hdr.desc_handle; 44901ae08745Sheppo 44911ae08745Sheppo if (idx >= VSW_RING_NUM_EL) { 44921ae08745Sheppo cmn_err(CE_WARN, "%s: corrupted ACK received " 44931ae08745Sheppo "(idx %ld)", __func__, idx); 44941ae08745Sheppo return; 44951ae08745Sheppo } 44961ae08745Sheppo 44971ae08745Sheppo if ((dp = ldcp->lane_out.dringp) == NULL) { 44981ae08745Sheppo DERR(vswp, "%s: no dring found", __func__); 44991ae08745Sheppo return; 45001ae08745Sheppo } 45011ae08745Sheppo 45021ae08745Sheppo priv_addr = (vsw_private_desc_t *)dp->priv_addr; 45031ae08745Sheppo 45041ae08745Sheppo /* move to correct location in ring */ 45051ae08745Sheppo priv_addr += idx; 45061ae08745Sheppo 45071ae08745Sheppo /* 45081ae08745Sheppo * When we sent the in-band message to our peer we 45091ae08745Sheppo * marked the copy in our private ring as READY. We now 45101ae08745Sheppo * check that the descriptor we are being ACK'ed for is in 45111ae08745Sheppo * fact READY, i.e. it is one we have shared with our peer. 45121ae08745Sheppo */ 45131ae08745Sheppo if (priv_addr->dstate != VIO_DESC_READY) { 45141ae08745Sheppo cmn_err(CE_WARN, "%s: (%ld) desc at index %ld not " 45151ae08745Sheppo "READY (0x%lx)", __func__, ldcp->ldc_id, idx, 45161ae08745Sheppo priv_addr->dstate); 45171ae08745Sheppo cmn_err(CE_CONT, "%s: bound %d: ncookies %ld\n", 45181ae08745Sheppo __func__, priv_addr->bound, 45191ae08745Sheppo priv_addr->ncookies); 45201ae08745Sheppo cmn_err(CE_CONT, "datalen %ld\n", priv_addr->datalen); 45211ae08745Sheppo return; 45221ae08745Sheppo } else { 45231ae08745Sheppo D2(vswp, "%s: (%lld) freeing descp at %lld", __func__, 45241ae08745Sheppo ldcp->ldc_id, idx); 45251ae08745Sheppo 45261ae08745Sheppo /* release resources associated with sent msg */ 45271ae08745Sheppo bzero(priv_addr->datap, priv_addr->datalen); 45281ae08745Sheppo priv_addr->datalen = 0; 45291ae08745Sheppo priv_addr->dstate = VIO_DESC_FREE; 45301ae08745Sheppo } 45311ae08745Sheppo break; 45321ae08745Sheppo 45331ae08745Sheppo case VIO_SUBTYPE_NACK: 45341ae08745Sheppo DERR(vswp, "%s: VIO_SUBTYPE_NACK", __func__); 45351ae08745Sheppo 45361ae08745Sheppo /* 45371ae08745Sheppo * We should only get a NACK if our peer doesn't like 45381ae08745Sheppo * something about a message we have sent it. If this 45391ae08745Sheppo * happens we just release the resources associated with 45401ae08745Sheppo * the message. (We are relying on higher layers to decide 45411ae08745Sheppo * whether or not to resend. 45421ae08745Sheppo */ 45431ae08745Sheppo 45441ae08745Sheppo /* limit check */ 45451ae08745Sheppo idx = ibnd_desc->hdr.desc_handle; 45461ae08745Sheppo 45471ae08745Sheppo if (idx >= VSW_RING_NUM_EL) { 45481ae08745Sheppo DERR(vswp, "%s: corrupted NACK received (idx %lld)", 45491ae08745Sheppo __func__, idx); 45501ae08745Sheppo return; 45511ae08745Sheppo } 45521ae08745Sheppo 45531ae08745Sheppo if ((dp = ldcp->lane_out.dringp) == NULL) { 45541ae08745Sheppo DERR(vswp, "%s: no dring found", __func__); 45551ae08745Sheppo return; 45561ae08745Sheppo } 45571ae08745Sheppo 45581ae08745Sheppo priv_addr = (vsw_private_desc_t *)dp->priv_addr; 45591ae08745Sheppo 45601ae08745Sheppo /* move to correct location in ring */ 45611ae08745Sheppo priv_addr += idx; 45621ae08745Sheppo 45631ae08745Sheppo /* release resources associated with sent msg */ 45641ae08745Sheppo bzero(priv_addr->datap, priv_addr->datalen); 45651ae08745Sheppo priv_addr->datalen = 0; 45661ae08745Sheppo priv_addr->dstate = VIO_DESC_FREE; 45671ae08745Sheppo 45681ae08745Sheppo break; 45691ae08745Sheppo 45701ae08745Sheppo default: 45711ae08745Sheppo DERR(vswp, "%s(%lld): Unknown vio_subtype %x\n", __func__, 45721ae08745Sheppo ldcp->ldc_id, ibnd_desc->hdr.tag.vio_subtype); 45731ae08745Sheppo } 45741ae08745Sheppo 45751ae08745Sheppo D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id); 45761ae08745Sheppo } 45771ae08745Sheppo 45781ae08745Sheppo static void 45791ae08745Sheppo vsw_process_err_pkt(vsw_ldc_t *ldcp, void *epkt, vio_msg_tag_t tag) 45801ae08745Sheppo { 45811ae08745Sheppo _NOTE(ARGUNUSED(epkt)) 45821ae08745Sheppo 45831ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 45841ae08745Sheppo uint16_t env = tag.vio_subtype_env; 45851ae08745Sheppo 45861ae08745Sheppo D1(vswp, "%s (%lld): enter\n", __func__, ldcp->ldc_id); 45871ae08745Sheppo 45881ae08745Sheppo /* 45891ae08745Sheppo * Error vio_subtypes have yet to be defined. So for 45901ae08745Sheppo * the moment we can't do anything. 45911ae08745Sheppo */ 45921ae08745Sheppo D2(vswp, "%s: (%x) vio_subtype env", __func__, env); 45931ae08745Sheppo 45941ae08745Sheppo D1(vswp, "%s (%lld): exit\n", __func__, ldcp->ldc_id); 45951ae08745Sheppo } 45961ae08745Sheppo 45971ae08745Sheppo /* 45981ae08745Sheppo * Switch the given ethernet frame when operating in layer 2 mode. 45991ae08745Sheppo * 46001ae08745Sheppo * vswp: pointer to the vsw instance 46011ae08745Sheppo * mp: pointer to chain of ethernet frame(s) to be switched 46021ae08745Sheppo * caller: identifies the source of this frame as: 46031ae08745Sheppo * 1. VSW_VNETPORT - a vsw port (connected to a vnet). 46041ae08745Sheppo * 2. VSW_PHYSDEV - the physical ethernet device 46051ae08745Sheppo * 3. VSW_LOCALDEV - vsw configured as a virtual interface 46061ae08745Sheppo * arg: argument provided by the caller. 46071ae08745Sheppo * 1. for VNETPORT - pointer to the corresponding vsw_port_t. 46081ae08745Sheppo * 2. for PHYSDEV - NULL 46091ae08745Sheppo * 3. for LOCALDEV - pointer to to this vsw_t(self) 46101ae08745Sheppo */ 46111ae08745Sheppo void 46121ae08745Sheppo vsw_switch_l2_frame(vsw_t *vswp, mblk_t *mp, int caller, 46131ae08745Sheppo vsw_port_t *arg, mac_resource_handle_t mrh) 46141ae08745Sheppo { 46151ae08745Sheppo struct ether_header *ehp; 46161ae08745Sheppo vsw_port_t *port = NULL; 46171ae08745Sheppo mblk_t *bp, *ret_m; 46181ae08745Sheppo mblk_t *nmp = NULL; 46191ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 46201ae08745Sheppo 46211ae08745Sheppo D1(vswp, "%s: enter (caller %d)", __func__, caller); 46221ae08745Sheppo 46231ae08745Sheppo /* 46241ae08745Sheppo * PERF: rather than breaking up the chain here, scan it 46251ae08745Sheppo * to find all mblks heading to same destination and then 46261ae08745Sheppo * pass that sub-chain to the lower transmit functions. 46271ae08745Sheppo */ 46281ae08745Sheppo 46291ae08745Sheppo /* process the chain of packets */ 46301ae08745Sheppo bp = mp; 46311ae08745Sheppo while (bp) { 46321ae08745Sheppo mp = bp; 46331ae08745Sheppo bp = bp->b_next; 46341ae08745Sheppo mp->b_next = mp->b_prev = NULL; 46351ae08745Sheppo ehp = (struct ether_header *)mp->b_rptr; 46361ae08745Sheppo 46371ae08745Sheppo D2(vswp, "%s: mblk data buffer %lld : actual data size %lld", 46381ae08745Sheppo __func__, MBLKSIZE(mp), MBLKL(mp)); 46391ae08745Sheppo 46401ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 46411ae08745Sheppo if (ether_cmp(&ehp->ether_dhost, &vswp->if_addr) == 0) { 46421ae08745Sheppo /* 46431ae08745Sheppo * If destination is VSW_LOCALDEV (vsw as an eth 46441ae08745Sheppo * interface) and if the device is up & running, 46451ae08745Sheppo * send the packet up the stack on this host. 46461ae08745Sheppo * If the virtual interface is down, drop the packet. 46471ae08745Sheppo */ 46481ae08745Sheppo if (caller != VSW_LOCALDEV) { 46491ae08745Sheppo if (vswp->if_state & VSW_IF_UP) { 46501ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 4651*ba2e4443Sseb mac_rx(vswp->if_mh, mrh, mp); 46521ae08745Sheppo } else { 46531ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 46541ae08745Sheppo /* Interface down, drop pkt */ 46551ae08745Sheppo freemsg(mp); 46561ae08745Sheppo } 46571ae08745Sheppo } else { 46581ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 46591ae08745Sheppo freemsg(mp); 46601ae08745Sheppo } 46611ae08745Sheppo continue; 46621ae08745Sheppo } 46631ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 46641ae08745Sheppo 46651ae08745Sheppo READ_ENTER(&plist->lockrw); 46661ae08745Sheppo port = vsw_lookup_fdb(vswp, ehp); 46671ae08745Sheppo if (port) { 46681ae08745Sheppo /* 46691ae08745Sheppo * Mark the port as in-use. 46701ae08745Sheppo */ 46711ae08745Sheppo mutex_enter(&port->ref_lock); 46721ae08745Sheppo port->ref_cnt++; 46731ae08745Sheppo mutex_exit(&port->ref_lock); 46741ae08745Sheppo RW_EXIT(&plist->lockrw); 46751ae08745Sheppo 46761ae08745Sheppo /* 46771ae08745Sheppo * If plumbed and in promisc mode then copy msg 46781ae08745Sheppo * and send up the stack. 46791ae08745Sheppo */ 46801ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 46811ae08745Sheppo if (VSW_U_P(vswp->if_state)) { 46821ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 46831ae08745Sheppo nmp = copymsg(mp); 46841ae08745Sheppo if (nmp) 4685*ba2e4443Sseb mac_rx(vswp->if_mh, mrh, nmp); 46861ae08745Sheppo } else { 46871ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 46881ae08745Sheppo } 46891ae08745Sheppo 46901ae08745Sheppo /* 46911ae08745Sheppo * If the destination is in FDB, the packet 46921ae08745Sheppo * should be forwarded to the correponding 46931ae08745Sheppo * vsw_port (connected to a vnet device - 46941ae08745Sheppo * VSW_VNETPORT) 46951ae08745Sheppo */ 46961ae08745Sheppo (void) vsw_portsend(port, mp); 46971ae08745Sheppo 46981ae08745Sheppo /* 46991ae08745Sheppo * Decrement use count in port and check if 47001ae08745Sheppo * should wake delete thread. 47011ae08745Sheppo */ 47021ae08745Sheppo mutex_enter(&port->ref_lock); 47031ae08745Sheppo port->ref_cnt--; 47041ae08745Sheppo if (port->ref_cnt == 0) 47051ae08745Sheppo cv_signal(&port->ref_cv); 47061ae08745Sheppo mutex_exit(&port->ref_lock); 47071ae08745Sheppo } else { 47081ae08745Sheppo RW_EXIT(&plist->lockrw); 47091ae08745Sheppo /* 47101ae08745Sheppo * Destination not in FDB. 47111ae08745Sheppo * 47121ae08745Sheppo * If the destination is broadcast or 47131ae08745Sheppo * multicast forward the packet to all 47141ae08745Sheppo * (VNETPORTs, PHYSDEV, LOCALDEV), 47151ae08745Sheppo * except the caller. 47161ae08745Sheppo */ 47171ae08745Sheppo if (IS_BROADCAST(ehp)) { 47181ae08745Sheppo D3(vswp, "%s: BROADCAST pkt", __func__); 47191ae08745Sheppo (void) vsw_forward_all(vswp, mp, 47201ae08745Sheppo caller, arg); 47211ae08745Sheppo } else if (IS_MULTICAST(ehp)) { 47221ae08745Sheppo D3(vswp, "%s: MULTICAST pkt", __func__); 47231ae08745Sheppo (void) vsw_forward_grp(vswp, mp, 47241ae08745Sheppo caller, arg); 47251ae08745Sheppo } else { 47261ae08745Sheppo /* 47271ae08745Sheppo * If the destination is unicast, and came 47281ae08745Sheppo * from either a logical network device or 47291ae08745Sheppo * the switch itself when it is plumbed, then 47301ae08745Sheppo * send it out on the physical device and also 47311ae08745Sheppo * up the stack if the logical interface is 47321ae08745Sheppo * in promiscious mode. 47331ae08745Sheppo * 47341ae08745Sheppo * NOTE: The assumption here is that if we 47351ae08745Sheppo * cannot find the destination in our fdb, its 47361ae08745Sheppo * a unicast address, and came from either a 47371ae08745Sheppo * vnet or down the stack (when plumbed) it 47381ae08745Sheppo * must be destinded for an ethernet device 47391ae08745Sheppo * outside our ldoms. 47401ae08745Sheppo */ 47411ae08745Sheppo if (caller == VSW_VNETPORT) { 47421ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 47431ae08745Sheppo if (VSW_U_P(vswp->if_state)) { 47441ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 47451ae08745Sheppo nmp = copymsg(mp); 47461ae08745Sheppo if (nmp) 4747*ba2e4443Sseb mac_rx(vswp->if_mh, 47481ae08745Sheppo mrh, nmp); 47491ae08745Sheppo } else { 47501ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 47511ae08745Sheppo } 47521ae08745Sheppo if ((ret_m = vsw_tx_msg(vswp, mp)) 47531ae08745Sheppo != NULL) { 47541ae08745Sheppo DERR(vswp, "%s: drop mblks to " 47551ae08745Sheppo "phys dev", __func__); 47561ae08745Sheppo freemsg(ret_m); 47571ae08745Sheppo } 47581ae08745Sheppo 47591ae08745Sheppo } else if (caller == VSW_PHYSDEV) { 47601ae08745Sheppo /* 47611ae08745Sheppo * Pkt seen because card in promisc 47621ae08745Sheppo * mode. Send up stack if plumbed in 47631ae08745Sheppo * promisc mode, else drop it. 47641ae08745Sheppo */ 47651ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 47661ae08745Sheppo if (VSW_U_P(vswp->if_state)) { 47671ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 4768*ba2e4443Sseb mac_rx(vswp->if_mh, mrh, mp); 47691ae08745Sheppo } else { 47701ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 47711ae08745Sheppo freemsg(mp); 47721ae08745Sheppo } 47731ae08745Sheppo 47741ae08745Sheppo } else if (caller == VSW_LOCALDEV) { 47751ae08745Sheppo /* 47761ae08745Sheppo * Pkt came down the stack, send out 47771ae08745Sheppo * over physical device. 47781ae08745Sheppo */ 47791ae08745Sheppo if ((ret_m = vsw_tx_msg(vswp, mp)) 47801ae08745Sheppo != NULL) { 47811ae08745Sheppo DERR(vswp, "%s: drop mblks to " 47821ae08745Sheppo "phys dev", __func__); 47831ae08745Sheppo freemsg(ret_m); 47841ae08745Sheppo } 47851ae08745Sheppo } 47861ae08745Sheppo } 47871ae08745Sheppo } 47881ae08745Sheppo } 47891ae08745Sheppo D1(vswp, "%s: exit\n", __func__); 47901ae08745Sheppo } 47911ae08745Sheppo 47921ae08745Sheppo /* 47931ae08745Sheppo * Switch ethernet frame when in layer 3 mode (i.e. using IP 47941ae08745Sheppo * layer to do the routing). 47951ae08745Sheppo * 47961ae08745Sheppo * There is a large amount of overlap between this function and 47971ae08745Sheppo * vsw_switch_l2_frame. At some stage we need to revisit and refactor 47981ae08745Sheppo * both these functions. 47991ae08745Sheppo */ 48001ae08745Sheppo void 48011ae08745Sheppo vsw_switch_l3_frame(vsw_t *vswp, mblk_t *mp, int caller, 48021ae08745Sheppo vsw_port_t *arg, mac_resource_handle_t mrh) 48031ae08745Sheppo { 48041ae08745Sheppo struct ether_header *ehp; 48051ae08745Sheppo vsw_port_t *port = NULL; 48061ae08745Sheppo mblk_t *bp = NULL; 48071ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 48081ae08745Sheppo 48091ae08745Sheppo D1(vswp, "%s: enter (caller %d)", __func__, caller); 48101ae08745Sheppo 48111ae08745Sheppo /* 48121ae08745Sheppo * In layer 3 mode should only ever be switching packets 48131ae08745Sheppo * between IP layer and vnet devices. So make sure thats 48141ae08745Sheppo * who is invoking us. 48151ae08745Sheppo */ 48161ae08745Sheppo if ((caller != VSW_LOCALDEV) && (caller != VSW_VNETPORT)) { 48171ae08745Sheppo DERR(vswp, "%s: unexpected caller (%d)", __func__, caller); 48181ae08745Sheppo freemsgchain(mp); 48191ae08745Sheppo return; 48201ae08745Sheppo } 48211ae08745Sheppo 48221ae08745Sheppo /* process the chain of packets */ 48231ae08745Sheppo bp = mp; 48241ae08745Sheppo while (bp) { 48251ae08745Sheppo mp = bp; 48261ae08745Sheppo bp = bp->b_next; 48271ae08745Sheppo mp->b_next = mp->b_prev = NULL; 48281ae08745Sheppo ehp = (struct ether_header *)mp->b_rptr; 48291ae08745Sheppo 48301ae08745Sheppo D2(vswp, "%s: mblk data buffer %lld : actual data size %lld", 48311ae08745Sheppo __func__, MBLKSIZE(mp), MBLKL(mp)); 48321ae08745Sheppo 48331ae08745Sheppo READ_ENTER(&plist->lockrw); 48341ae08745Sheppo port = vsw_lookup_fdb(vswp, ehp); 48351ae08745Sheppo if (port) { 48361ae08745Sheppo /* 48371ae08745Sheppo * Mark port as in-use. 48381ae08745Sheppo */ 48391ae08745Sheppo mutex_enter(&port->ref_lock); 48401ae08745Sheppo port->ref_cnt++; 48411ae08745Sheppo mutex_exit(&port->ref_lock); 48421ae08745Sheppo RW_EXIT(&plist->lockrw); 48431ae08745Sheppo 48441ae08745Sheppo D2(vswp, "%s: sending to target port", __func__); 48451ae08745Sheppo (void) vsw_portsend(port, mp); 48461ae08745Sheppo 48471ae08745Sheppo /* 48481ae08745Sheppo * Finished with port so decrement ref count and 48491ae08745Sheppo * check if should wake delete thread. 48501ae08745Sheppo */ 48511ae08745Sheppo mutex_enter(&port->ref_lock); 48521ae08745Sheppo port->ref_cnt--; 48531ae08745Sheppo if (port->ref_cnt == 0) 48541ae08745Sheppo cv_signal(&port->ref_cv); 48551ae08745Sheppo mutex_exit(&port->ref_lock); 48561ae08745Sheppo } else { 48571ae08745Sheppo RW_EXIT(&plist->lockrw); 48581ae08745Sheppo /* 48591ae08745Sheppo * Destination not in FDB 48601ae08745Sheppo * 48611ae08745Sheppo * If the destination is broadcast or 48621ae08745Sheppo * multicast forward the packet to all 48631ae08745Sheppo * (VNETPORTs, PHYSDEV, LOCALDEV), 48641ae08745Sheppo * except the caller. 48651ae08745Sheppo */ 48661ae08745Sheppo if (IS_BROADCAST(ehp)) { 48671ae08745Sheppo D2(vswp, "%s: BROADCAST pkt", __func__); 48681ae08745Sheppo (void) vsw_forward_all(vswp, mp, 48691ae08745Sheppo caller, arg); 48701ae08745Sheppo } else if (IS_MULTICAST(ehp)) { 48711ae08745Sheppo D2(vswp, "%s: MULTICAST pkt", __func__); 48721ae08745Sheppo (void) vsw_forward_grp(vswp, mp, 48731ae08745Sheppo caller, arg); 48741ae08745Sheppo } else { 48751ae08745Sheppo /* 48761ae08745Sheppo * Unicast pkt from vnet that we don't have 48771ae08745Sheppo * an FDB entry for, so must be destinded for 48781ae08745Sheppo * the outside world. Attempt to send up to the 48791ae08745Sheppo * IP layer to allow it to deal with it. 48801ae08745Sheppo */ 48811ae08745Sheppo if (caller == VSW_VNETPORT) { 48821ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 48831ae08745Sheppo if (vswp->if_state & VSW_IF_UP) { 48841ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 48851ae08745Sheppo D2(vswp, "%s: sending up", 48861ae08745Sheppo __func__); 4887*ba2e4443Sseb mac_rx(vswp->if_mh, mrh, mp); 48881ae08745Sheppo } else { 48891ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 48901ae08745Sheppo /* Interface down, drop pkt */ 48911ae08745Sheppo D2(vswp, "%s I/F down", 48921ae08745Sheppo __func__); 48931ae08745Sheppo freemsg(mp); 48941ae08745Sheppo } 48951ae08745Sheppo } 48961ae08745Sheppo } 48971ae08745Sheppo } 48981ae08745Sheppo } 48991ae08745Sheppo 49001ae08745Sheppo D1(vswp, "%s: exit", __func__); 49011ae08745Sheppo } 49021ae08745Sheppo 49031ae08745Sheppo /* 49041ae08745Sheppo * Forward the ethernet frame to all ports (VNETPORTs, PHYSDEV, LOCALDEV), 49051ae08745Sheppo * except the caller (port on which frame arrived). 49061ae08745Sheppo */ 49071ae08745Sheppo static int 49081ae08745Sheppo vsw_forward_all(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg) 49091ae08745Sheppo { 49101ae08745Sheppo vsw_port_list_t *plist = &vswp->plist; 49111ae08745Sheppo vsw_port_t *portp; 49121ae08745Sheppo mblk_t *nmp = NULL; 49131ae08745Sheppo mblk_t *ret_m = NULL; 49141ae08745Sheppo int skip_port = 0; 49151ae08745Sheppo 49161ae08745Sheppo D1(vswp, "vsw_forward_all: enter\n"); 49171ae08745Sheppo 49181ae08745Sheppo /* 49191ae08745Sheppo * Broadcast message from inside ldoms so send to outside 49201ae08745Sheppo * world if in either of layer 2 modes. 49211ae08745Sheppo */ 49221ae08745Sheppo if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) || 49231ae08745Sheppo (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) && 49241ae08745Sheppo ((caller == VSW_LOCALDEV) || (caller == VSW_VNETPORT))) { 49251ae08745Sheppo 49261ae08745Sheppo nmp = dupmsg(mp); 49271ae08745Sheppo if (nmp) { 49281ae08745Sheppo if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) { 49291ae08745Sheppo DERR(vswp, "%s: dropping pkt(s) " 49301ae08745Sheppo "consisting of %ld bytes of data for" 49311ae08745Sheppo " physical device", __func__, MBLKL(ret_m)); 49321ae08745Sheppo freemsg(ret_m); 49331ae08745Sheppo } 49341ae08745Sheppo } 49351ae08745Sheppo } 49361ae08745Sheppo 49371ae08745Sheppo if (caller == VSW_VNETPORT) 49381ae08745Sheppo skip_port = 1; 49391ae08745Sheppo 49401ae08745Sheppo /* 49411ae08745Sheppo * Broadcast message from other vnet (layer 2 or 3) or outside 49421ae08745Sheppo * world (layer 2 only), send up stack if plumbed. 49431ae08745Sheppo */ 49441ae08745Sheppo if ((caller == VSW_PHYSDEV) || (caller == VSW_VNETPORT)) { 49451ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 49461ae08745Sheppo if (vswp->if_state & VSW_IF_UP) { 49471ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 49481ae08745Sheppo nmp = copymsg(mp); 49491ae08745Sheppo if (nmp) 4950*ba2e4443Sseb mac_rx(vswp->if_mh, NULL, nmp); 49511ae08745Sheppo } else { 49521ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 49531ae08745Sheppo } 49541ae08745Sheppo } 49551ae08745Sheppo 49561ae08745Sheppo /* send it to all VNETPORTs */ 49571ae08745Sheppo READ_ENTER(&plist->lockrw); 49581ae08745Sheppo for (portp = plist->head; portp != NULL; portp = portp->p_next) { 49591ae08745Sheppo D2(vswp, "vsw_forward_all: port %d", portp->p_instance); 49601ae08745Sheppo /* 49611ae08745Sheppo * Caution ! - don't reorder these two checks as arg 49621ae08745Sheppo * will be NULL if the caller is PHYSDEV. skip_port is 49631ae08745Sheppo * only set if caller is VNETPORT. 49641ae08745Sheppo */ 49651ae08745Sheppo if ((skip_port) && (portp == arg)) 49661ae08745Sheppo continue; 49671ae08745Sheppo else { 49681ae08745Sheppo nmp = dupmsg(mp); 49691ae08745Sheppo if (nmp) { 49701ae08745Sheppo (void) vsw_portsend(portp, nmp); 49711ae08745Sheppo } else { 49721ae08745Sheppo DERR(vswp, "vsw_forward_all: nmp NULL"); 49731ae08745Sheppo } 49741ae08745Sheppo } 49751ae08745Sheppo } 49761ae08745Sheppo RW_EXIT(&plist->lockrw); 49771ae08745Sheppo 49781ae08745Sheppo freemsg(mp); 49791ae08745Sheppo 49801ae08745Sheppo D1(vswp, "vsw_forward_all: exit\n"); 49811ae08745Sheppo return (0); 49821ae08745Sheppo } 49831ae08745Sheppo 49841ae08745Sheppo /* 49851ae08745Sheppo * Forward pkts to any devices or interfaces which have registered 49861ae08745Sheppo * an interest in them (i.e. multicast groups). 49871ae08745Sheppo */ 49881ae08745Sheppo static int 49891ae08745Sheppo vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg) 49901ae08745Sheppo { 49911ae08745Sheppo struct ether_header *ehp = (struct ether_header *)mp->b_rptr; 49921ae08745Sheppo mfdb_ent_t *entp = NULL; 49931ae08745Sheppo mfdb_ent_t *tpp = NULL; 49941ae08745Sheppo vsw_port_t *port; 49951ae08745Sheppo uint64_t key = 0; 49961ae08745Sheppo mblk_t *nmp = NULL; 49971ae08745Sheppo mblk_t *ret_m = NULL; 49981ae08745Sheppo boolean_t check_if = B_TRUE; 49991ae08745Sheppo 50001ae08745Sheppo /* 50011ae08745Sheppo * Convert address to hash table key 50021ae08745Sheppo */ 50031ae08745Sheppo KEY_HASH(key, ehp->ether_dhost); 50041ae08745Sheppo 50051ae08745Sheppo D1(vswp, "%s: key 0x%llx", __func__, key); 50061ae08745Sheppo 50071ae08745Sheppo /* 50081ae08745Sheppo * If pkt came from either a vnet or down the stack (if we are 50091ae08745Sheppo * plumbed) and we are in layer 2 mode, then we send the pkt out 50101ae08745Sheppo * over the physical adapter, and then check to see if any other 50111ae08745Sheppo * vnets are interested in it. 50121ae08745Sheppo */ 50131ae08745Sheppo if (((vswp->smode[vswp->smode_idx] == VSW_LAYER2) || 50141ae08745Sheppo (vswp->smode[vswp->smode_idx] == VSW_LAYER2_PROMISC)) && 50151ae08745Sheppo ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) { 50161ae08745Sheppo nmp = dupmsg(mp); 50171ae08745Sheppo if (nmp) { 50181ae08745Sheppo if ((ret_m = vsw_tx_msg(vswp, nmp)) != NULL) { 50191ae08745Sheppo DERR(vswp, "%s: dropping pkt(s) " 50201ae08745Sheppo "consisting of %ld bytes of " 50211ae08745Sheppo "data for physical device", 50221ae08745Sheppo __func__, MBLKL(ret_m)); 50231ae08745Sheppo freemsg(ret_m); 50241ae08745Sheppo } 50251ae08745Sheppo } 50261ae08745Sheppo } 50271ae08745Sheppo 50281ae08745Sheppo READ_ENTER(&vswp->mfdbrw); 50291ae08745Sheppo if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key, 50301ae08745Sheppo (mod_hash_val_t *)&entp) != 0) { 50311ae08745Sheppo D3(vswp, "%s: no table entry found for addr 0x%llx", 50321ae08745Sheppo __func__, key); 50331ae08745Sheppo } else { 50341ae08745Sheppo /* 50351ae08745Sheppo * Send to list of devices associated with this address... 50361ae08745Sheppo */ 50371ae08745Sheppo for (tpp = entp; tpp != NULL; tpp = tpp->nextp) { 50381ae08745Sheppo 50391ae08745Sheppo /* dont send to ourselves */ 50401ae08745Sheppo if ((caller == VSW_VNETPORT) && 50411ae08745Sheppo (tpp->d_addr == (void *)arg)) { 50421ae08745Sheppo port = (vsw_port_t *)tpp->d_addr; 50431ae08745Sheppo D3(vswp, "%s: not sending to ourselves" 50441ae08745Sheppo " : port %d", __func__, 50451ae08745Sheppo port->p_instance); 50461ae08745Sheppo continue; 50471ae08745Sheppo 50481ae08745Sheppo } else if ((caller == VSW_LOCALDEV) && 50491ae08745Sheppo (tpp->d_type == VSW_LOCALDEV)) { 50501ae08745Sheppo D3(vswp, "%s: not sending back up stack", 50511ae08745Sheppo __func__); 50521ae08745Sheppo continue; 50531ae08745Sheppo } 50541ae08745Sheppo 50551ae08745Sheppo if (tpp->d_type == VSW_VNETPORT) { 50561ae08745Sheppo port = (vsw_port_t *)tpp->d_addr; 50571ae08745Sheppo D3(vswp, "%s: sending to port %ld for " 50581ae08745Sheppo " addr 0x%llx", __func__, 50591ae08745Sheppo port->p_instance, key); 50601ae08745Sheppo 50611ae08745Sheppo nmp = dupmsg(mp); 50621ae08745Sheppo if (nmp) 50631ae08745Sheppo (void) vsw_portsend(port, nmp); 50641ae08745Sheppo } else { 50651ae08745Sheppo if (vswp->if_state & VSW_IF_UP) { 50661ae08745Sheppo nmp = copymsg(mp); 50671ae08745Sheppo if (nmp) 5068*ba2e4443Sseb mac_rx(vswp->if_mh, NULL, nmp); 50691ae08745Sheppo check_if = B_FALSE; 50701ae08745Sheppo D3(vswp, "%s: sending up stack" 50711ae08745Sheppo " for addr 0x%llx", __func__, 50721ae08745Sheppo key); 50731ae08745Sheppo } 50741ae08745Sheppo } 50751ae08745Sheppo } 50761ae08745Sheppo } 50771ae08745Sheppo 50781ae08745Sheppo RW_EXIT(&vswp->mfdbrw); 50791ae08745Sheppo 50801ae08745Sheppo /* 50811ae08745Sheppo * If the pkt came from either a vnet or from physical device, 50821ae08745Sheppo * and if we havent already sent the pkt up the stack then we 50831ae08745Sheppo * check now if we can/should (i.e. the interface is plumbed 50841ae08745Sheppo * and in promisc mode). 50851ae08745Sheppo */ 50861ae08745Sheppo if ((check_if) && 50871ae08745Sheppo ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) { 50881ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 50891ae08745Sheppo if (VSW_U_P(vswp->if_state)) { 50901ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 50911ae08745Sheppo D3(vswp, "%s: (caller %d) finally sending up stack" 50921ae08745Sheppo " for addr 0x%llx", __func__, caller, key); 50931ae08745Sheppo nmp = copymsg(mp); 50941ae08745Sheppo if (nmp) 5095*ba2e4443Sseb mac_rx(vswp->if_mh, NULL, nmp); 50961ae08745Sheppo } else { 50971ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 50981ae08745Sheppo } 50991ae08745Sheppo } 51001ae08745Sheppo 51011ae08745Sheppo freemsg(mp); 51021ae08745Sheppo 51031ae08745Sheppo D1(vswp, "%s: exit", __func__); 51041ae08745Sheppo 51051ae08745Sheppo return (0); 51061ae08745Sheppo } 51071ae08745Sheppo 51081ae08745Sheppo /* transmit the packet over the given port */ 51091ae08745Sheppo static int 51101ae08745Sheppo vsw_portsend(vsw_port_t *port, mblk_t *mp) 51111ae08745Sheppo { 51121ae08745Sheppo vsw_ldc_list_t *ldcl = &port->p_ldclist; 51131ae08745Sheppo vsw_ldc_t *ldcp; 51141ae08745Sheppo int status = 0; 51151ae08745Sheppo 51161ae08745Sheppo 51171ae08745Sheppo READ_ENTER(&ldcl->lockrw); 51181ae08745Sheppo /* 51191ae08745Sheppo * Note for now, we have a single channel. 51201ae08745Sheppo */ 51211ae08745Sheppo ldcp = ldcl->head; 51221ae08745Sheppo if (ldcp == NULL) { 51231ae08745Sheppo DERR(port->p_vswp, "vsw_portsend: no ldc: dropping packet\n"); 51241ae08745Sheppo freemsg(mp); 51251ae08745Sheppo RW_EXIT(&ldcl->lockrw); 51261ae08745Sheppo return (1); 51271ae08745Sheppo } 51281ae08745Sheppo 51291ae08745Sheppo /* 51301ae08745Sheppo * Send the message out using the appropriate 51311ae08745Sheppo * transmit function which will free mblock when it 51321ae08745Sheppo * is finished with it. 51331ae08745Sheppo */ 51341ae08745Sheppo mutex_enter(&port->tx_lock); 51351ae08745Sheppo if (port->transmit != NULL) 51361ae08745Sheppo status = (*port->transmit)(ldcp, mp); 51371ae08745Sheppo else { 51381ae08745Sheppo freemsg(mp); 51391ae08745Sheppo } 51401ae08745Sheppo mutex_exit(&port->tx_lock); 51411ae08745Sheppo 51421ae08745Sheppo RW_EXIT(&ldcl->lockrw); 51431ae08745Sheppo 51441ae08745Sheppo return (status); 51451ae08745Sheppo } 51461ae08745Sheppo 51471ae08745Sheppo /* 51481ae08745Sheppo * Send packet out via descriptor ring to a logical device. 51491ae08745Sheppo */ 51501ae08745Sheppo static int 51511ae08745Sheppo vsw_dringsend(vsw_ldc_t *ldcp, mblk_t *mp) 51521ae08745Sheppo { 51531ae08745Sheppo vio_dring_msg_t dring_pkt; 51541ae08745Sheppo dring_info_t *dp = NULL; 51551ae08745Sheppo vsw_private_desc_t *priv_desc = NULL; 51561ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 51571ae08745Sheppo mblk_t *bp; 51581ae08745Sheppo size_t n, size; 51591ae08745Sheppo caddr_t bufp; 51601ae08745Sheppo int idx; 51611ae08745Sheppo int status = LDC_TX_SUCCESS; 51621ae08745Sheppo 51631ae08745Sheppo D1(vswp, "%s(%lld): enter\n", __func__, ldcp->ldc_id); 51641ae08745Sheppo 51651ae08745Sheppo /* TODO: make test a macro */ 51661ae08745Sheppo if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) || 51671ae08745Sheppo (ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == NULL)) { 51681ae08745Sheppo DWARN(vswp, "%s(%lld) status(%d) lstate(0x%llx), dropping " 51691ae08745Sheppo "packet\n", __func__, ldcp->ldc_id, ldcp->ldc_status, 51701ae08745Sheppo ldcp->lane_out.lstate); 51711ae08745Sheppo freemsg(mp); 51721ae08745Sheppo return (LDC_TX_FAILURE); 51731ae08745Sheppo } 51741ae08745Sheppo 51751ae08745Sheppo /* 51761ae08745Sheppo * Note - using first ring only, this may change 51771ae08745Sheppo * in the future. 51781ae08745Sheppo */ 51791ae08745Sheppo if ((dp = ldcp->lane_out.dringp) == NULL) { 51801ae08745Sheppo DERR(vswp, "%s(%lld): no dring for outbound lane on" 51811ae08745Sheppo " channel %d", __func__, ldcp->ldc_id, ldcp->ldc_id); 51821ae08745Sheppo freemsg(mp); 51831ae08745Sheppo return (LDC_TX_FAILURE); 51841ae08745Sheppo } 51851ae08745Sheppo 51861ae08745Sheppo mutex_enter(&dp->dlock); 51871ae08745Sheppo 51881ae08745Sheppo size = msgsize(mp); 51891ae08745Sheppo if (size > (size_t)ETHERMAX) { 51901ae08745Sheppo DERR(vswp, "%s(%lld) invalid size (%ld)\n", __func__, 51911ae08745Sheppo ldcp->ldc_id, size); 51921ae08745Sheppo status = LDC_TX_FAILURE; 51931ae08745Sheppo goto vsw_dringsend_free_exit; 51941ae08745Sheppo } 51951ae08745Sheppo 51961ae08745Sheppo /* 51971ae08745Sheppo * Find a free descriptor 51981ae08745Sheppo * 51991ae08745Sheppo * Note: for the moment we are assuming that we will only 52001ae08745Sheppo * have one dring going from the switch to each of its 52011ae08745Sheppo * peers. This may change in the future. 52021ae08745Sheppo */ 52031ae08745Sheppo if (vsw_dring_find_free_desc(dp, &priv_desc, &idx) != 0) { 52041ae08745Sheppo DERR(vswp, "%s(%lld): no descriptor available for ring " 52051ae08745Sheppo "at 0x%llx", __func__, ldcp->ldc_id, dp); 52061ae08745Sheppo 52071ae08745Sheppo /* nothing more we can do */ 52081ae08745Sheppo status = LDC_TX_NORESOURCES; 52091ae08745Sheppo goto vsw_dringsend_free_exit; 52101ae08745Sheppo } else { 52111ae08745Sheppo D2(vswp, "%s(%lld): free private descriptor found at pos " 52121ae08745Sheppo "%ld addr 0x%llx\n", __func__, ldcp->ldc_id, idx, 52131ae08745Sheppo priv_desc); 52141ae08745Sheppo } 52151ae08745Sheppo 52161ae08745Sheppo /* copy data into the descriptor */ 52171ae08745Sheppo bufp = priv_desc->datap; 52181ae08745Sheppo for (bp = mp, n = 0; bp != NULL; bp = bp->b_cont) { 52191ae08745Sheppo n = MBLKL(bp); 52201ae08745Sheppo bcopy(bp->b_rptr, bufp, n); 52211ae08745Sheppo bufp += n; 52221ae08745Sheppo } 52231ae08745Sheppo 52241ae08745Sheppo priv_desc->datalen = (size < (size_t)ETHERMIN) ? ETHERMIN : size; 52251ae08745Sheppo priv_desc->dstate = VIO_DESC_READY; 52261ae08745Sheppo 52271ae08745Sheppo /* 52281ae08745Sheppo * Copy relevant sections of private descriptor 52291ae08745Sheppo * to public section 52301ae08745Sheppo */ 52311ae08745Sheppo vsw_dring_priv2pub(priv_desc); 52321ae08745Sheppo 52331ae08745Sheppo /* 52341ae08745Sheppo * Send a vio_dring_msg to peer to prompt them to read 52351ae08745Sheppo * the updated descriptor ring. 52361ae08745Sheppo */ 52371ae08745Sheppo dring_pkt.tag.vio_msgtype = VIO_TYPE_DATA; 52381ae08745Sheppo dring_pkt.tag.vio_subtype = VIO_SUBTYPE_INFO; 52391ae08745Sheppo dring_pkt.tag.vio_subtype_env = VIO_DRING_DATA; 52401ae08745Sheppo dring_pkt.tag.vio_sid = ldcp->local_session; 52411ae08745Sheppo 52421ae08745Sheppo /* Note - for now using first ring */ 52431ae08745Sheppo dring_pkt.dring_ident = dp->ident; 52441ae08745Sheppo 52451ae08745Sheppo /* 52461ae08745Sheppo * Access to the seq_num is implicitly protected by the 52471ae08745Sheppo * fact that we have only one dring associated with the 52481ae08745Sheppo * lane currently and we hold the associated dring lock. 52491ae08745Sheppo */ 52501ae08745Sheppo dring_pkt.seq_num = ldcp->lane_out.seq_num++; 52511ae08745Sheppo 52521ae08745Sheppo /* Note - only updating single descrip at time at the moment */ 52531ae08745Sheppo dring_pkt.start_idx = idx; 52541ae08745Sheppo dring_pkt.end_idx = idx; 52551ae08745Sheppo 52561ae08745Sheppo D3(vswp, "%s(%lld): dring 0x%llx : ident 0x%llx\n", __func__, 52571ae08745Sheppo ldcp->ldc_id, dp, dring_pkt.dring_ident); 52581ae08745Sheppo D3(vswp, "%s(%lld): start %lld : end %lld : seq %lld\n", __func__, 52591ae08745Sheppo ldcp->ldc_id, dring_pkt.start_idx, dring_pkt.end_idx, 52601ae08745Sheppo dring_pkt.seq_num); 52611ae08745Sheppo 52621ae08745Sheppo vsw_send_msg(ldcp, (void *)&dring_pkt, sizeof (vio_dring_msg_t)); 52631ae08745Sheppo 52641ae08745Sheppo vsw_dringsend_free_exit: 52651ae08745Sheppo 52661ae08745Sheppo mutex_exit(&dp->dlock); 52671ae08745Sheppo 52681ae08745Sheppo /* free the message block */ 52691ae08745Sheppo freemsg(mp); 52701ae08745Sheppo 52711ae08745Sheppo D1(vswp, "%s(%lld): exit\n", __func__, ldcp->ldc_id); 52721ae08745Sheppo return (status); 52731ae08745Sheppo } 52741ae08745Sheppo 52751ae08745Sheppo /* 52761ae08745Sheppo * Send an in-band descriptor message over ldc. 52771ae08745Sheppo */ 52781ae08745Sheppo static int 52791ae08745Sheppo vsw_descrsend(vsw_ldc_t *ldcp, mblk_t *mp) 52801ae08745Sheppo { 52811ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 52821ae08745Sheppo vio_ibnd_desc_t ibnd_msg; 52831ae08745Sheppo vsw_private_desc_t *priv_desc = NULL; 52841ae08745Sheppo dring_info_t *dp = NULL; 52851ae08745Sheppo size_t n, size = 0; 52861ae08745Sheppo caddr_t bufp; 52871ae08745Sheppo mblk_t *bp; 52881ae08745Sheppo int idx, i; 52891ae08745Sheppo int status = LDC_TX_SUCCESS; 52901ae08745Sheppo static int warn_msg = 1; 52911ae08745Sheppo 52921ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 52931ae08745Sheppo 52941ae08745Sheppo ASSERT(mp != NULL); 52951ae08745Sheppo 52961ae08745Sheppo if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) || 52971ae08745Sheppo (ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == NULL)) { 52981ae08745Sheppo DERR(vswp, "%s(%lld) status(%d) state (0x%llx), dropping pkt", 52991ae08745Sheppo __func__, ldcp->ldc_id, ldcp->ldc_status, 53001ae08745Sheppo ldcp->lane_out.lstate); 53011ae08745Sheppo freemsg(mp); 53021ae08745Sheppo return (LDC_TX_FAILURE); 53031ae08745Sheppo } 53041ae08745Sheppo 53051ae08745Sheppo /* 53061ae08745Sheppo * only expect single dring to exist, which we use 53071ae08745Sheppo * as an internal buffer, rather than a transfer channel. 53081ae08745Sheppo */ 53091ae08745Sheppo if ((dp = ldcp->lane_out.dringp) == NULL) { 53101ae08745Sheppo DERR(vswp, "%s(%lld): no dring for outbound lane", 53111ae08745Sheppo __func__, ldcp->ldc_id); 53121ae08745Sheppo DERR(vswp, "%s(%lld) status(%d) state (0x%llx)", 53131ae08745Sheppo __func__, ldcp->ldc_id, ldcp->ldc_status, 53141ae08745Sheppo ldcp->lane_out.lstate); 53151ae08745Sheppo freemsg(mp); 53161ae08745Sheppo return (LDC_TX_FAILURE); 53171ae08745Sheppo } 53181ae08745Sheppo 53191ae08745Sheppo mutex_enter(&dp->dlock); 53201ae08745Sheppo 53211ae08745Sheppo size = msgsize(mp); 53221ae08745Sheppo if (size > (size_t)ETHERMAX) { 53231ae08745Sheppo DERR(vswp, "%s(%lld) invalid size (%ld)\n", __func__, 53241ae08745Sheppo ldcp->ldc_id, size); 53251ae08745Sheppo status = LDC_TX_FAILURE; 53261ae08745Sheppo goto vsw_descrsend_free_exit; 53271ae08745Sheppo } 53281ae08745Sheppo 53291ae08745Sheppo /* 53301ae08745Sheppo * Find a free descriptor in our buffer ring 53311ae08745Sheppo */ 53321ae08745Sheppo if (vsw_dring_find_free_desc(dp, &priv_desc, &idx) != 0) { 53331ae08745Sheppo if (warn_msg) { 53341ae08745Sheppo DERR(vswp, "%s(%lld): no descriptor available for ring " 53351ae08745Sheppo "at 0x%llx", __func__, ldcp->ldc_id, dp); 53361ae08745Sheppo warn_msg = 0; 53371ae08745Sheppo } 53381ae08745Sheppo 53391ae08745Sheppo /* nothing more we can do */ 53401ae08745Sheppo status = LDC_TX_NORESOURCES; 53411ae08745Sheppo goto vsw_descrsend_free_exit; 53421ae08745Sheppo } else { 53431ae08745Sheppo D2(vswp, "%s(%lld): free private descriptor found at pos " 53441ae08745Sheppo "%ld addr 0x%x\n", __func__, ldcp->ldc_id, idx, 53451ae08745Sheppo priv_desc); 53461ae08745Sheppo warn_msg = 1; 53471ae08745Sheppo } 53481ae08745Sheppo 53491ae08745Sheppo /* copy data into the descriptor */ 53501ae08745Sheppo bufp = priv_desc->datap; 53511ae08745Sheppo for (bp = mp, n = 0; bp != NULL; bp = bp->b_cont) { 53521ae08745Sheppo n = MBLKL(bp); 53531ae08745Sheppo bcopy(bp->b_rptr, bufp, n); 53541ae08745Sheppo bufp += n; 53551ae08745Sheppo } 53561ae08745Sheppo 53571ae08745Sheppo priv_desc->datalen = (size < (size_t)ETHERMIN) ? ETHERMIN : size; 53581ae08745Sheppo priv_desc->dstate = VIO_DESC_READY; 53591ae08745Sheppo 53601ae08745Sheppo /* create and send the in-band descp msg */ 53611ae08745Sheppo ibnd_msg.hdr.tag.vio_msgtype = VIO_TYPE_DATA; 53621ae08745Sheppo ibnd_msg.hdr.tag.vio_subtype = VIO_SUBTYPE_INFO; 53631ae08745Sheppo ibnd_msg.hdr.tag.vio_subtype_env = VIO_DESC_DATA; 53641ae08745Sheppo ibnd_msg.hdr.tag.vio_sid = ldcp->local_session; 53651ae08745Sheppo 53661ae08745Sheppo /* 53671ae08745Sheppo * Access to the seq_num is implicitly protected by the 53681ae08745Sheppo * fact that we have only one dring associated with the 53691ae08745Sheppo * lane currently and we hold the associated dring lock. 53701ae08745Sheppo */ 53711ae08745Sheppo ibnd_msg.hdr.seq_num = ldcp->lane_out.seq_num++; 53721ae08745Sheppo 53731ae08745Sheppo /* 53741ae08745Sheppo * Copy the mem cookies describing the data from the 53751ae08745Sheppo * private region of the descriptor ring into the inband 53761ae08745Sheppo * descriptor. 53771ae08745Sheppo */ 53781ae08745Sheppo for (i = 0; i < priv_desc->ncookies; i++) { 53791ae08745Sheppo bcopy(&priv_desc->memcookie[i], &ibnd_msg.memcookie[i], 53801ae08745Sheppo sizeof (ldc_mem_cookie_t)); 53811ae08745Sheppo } 53821ae08745Sheppo 53831ae08745Sheppo ibnd_msg.hdr.desc_handle = idx; 53841ae08745Sheppo ibnd_msg.ncookies = priv_desc->ncookies; 53851ae08745Sheppo ibnd_msg.nbytes = size; 53861ae08745Sheppo 53871ae08745Sheppo vsw_send_msg(ldcp, (void *)&ibnd_msg, sizeof (vio_ibnd_desc_t)); 53881ae08745Sheppo 53891ae08745Sheppo vsw_descrsend_free_exit: 53901ae08745Sheppo 53911ae08745Sheppo mutex_exit(&dp->dlock); 53921ae08745Sheppo 53931ae08745Sheppo /* free the allocated message blocks */ 53941ae08745Sheppo freemsg(mp); 53951ae08745Sheppo 53961ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 53971ae08745Sheppo return (status); 53981ae08745Sheppo } 53991ae08745Sheppo 54001ae08745Sheppo static void 54011ae08745Sheppo vsw_send_ver(vsw_ldc_t *ldcp) 54021ae08745Sheppo { 54031ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 54041ae08745Sheppo lane_t *lp = &ldcp->lane_out; 54051ae08745Sheppo vio_ver_msg_t ver_msg; 54061ae08745Sheppo 54071ae08745Sheppo D1(vswp, "%s enter", __func__); 54081ae08745Sheppo 54091ae08745Sheppo ver_msg.tag.vio_msgtype = VIO_TYPE_CTRL; 54101ae08745Sheppo ver_msg.tag.vio_subtype = VIO_SUBTYPE_INFO; 54111ae08745Sheppo ver_msg.tag.vio_subtype_env = VIO_VER_INFO; 54121ae08745Sheppo ver_msg.tag.vio_sid = ldcp->local_session; 54131ae08745Sheppo 54141ae08745Sheppo ver_msg.ver_major = vsw_versions[0].ver_major; 54151ae08745Sheppo ver_msg.ver_minor = vsw_versions[0].ver_minor; 54161ae08745Sheppo ver_msg.dev_class = VDEV_NETWORK_SWITCH; 54171ae08745Sheppo 54181ae08745Sheppo lp->lstate |= VSW_VER_INFO_SENT; 54191ae08745Sheppo lp->ver_major = ver_msg.ver_major; 54201ae08745Sheppo lp->ver_minor = ver_msg.ver_minor; 54211ae08745Sheppo 54221ae08745Sheppo DUMP_TAG(ver_msg.tag); 54231ae08745Sheppo 54241ae08745Sheppo vsw_send_msg(ldcp, &ver_msg, sizeof (vio_ver_msg_t)); 54251ae08745Sheppo 54261ae08745Sheppo D1(vswp, "%s (%d): exit", __func__, ldcp->ldc_id); 54271ae08745Sheppo } 54281ae08745Sheppo 54291ae08745Sheppo static void 54301ae08745Sheppo vsw_send_attr(vsw_ldc_t *ldcp) 54311ae08745Sheppo { 54321ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 54331ae08745Sheppo lane_t *lp = &ldcp->lane_out; 54341ae08745Sheppo vnet_attr_msg_t attr_msg; 54351ae08745Sheppo 54361ae08745Sheppo D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id); 54371ae08745Sheppo 54381ae08745Sheppo /* 54391ae08745Sheppo * Subtype is set to INFO by default 54401ae08745Sheppo */ 54411ae08745Sheppo attr_msg.tag.vio_msgtype = VIO_TYPE_CTRL; 54421ae08745Sheppo attr_msg.tag.vio_subtype = VIO_SUBTYPE_INFO; 54431ae08745Sheppo attr_msg.tag.vio_subtype_env = VIO_ATTR_INFO; 54441ae08745Sheppo attr_msg.tag.vio_sid = ldcp->local_session; 54451ae08745Sheppo 54461ae08745Sheppo /* payload copied from default settings for lane */ 54471ae08745Sheppo attr_msg.mtu = lp->mtu; 54481ae08745Sheppo attr_msg.addr_type = lp->addr_type; 54491ae08745Sheppo attr_msg.xfer_mode = lp->xfer_mode; 54501ae08745Sheppo attr_msg.ack_freq = lp->xfer_mode; 54511ae08745Sheppo 54521ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 54531ae08745Sheppo bcopy(&(vswp->if_addr), &(attr_msg.addr), ETHERADDRL); 54541ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 54551ae08745Sheppo 54561ae08745Sheppo ldcp->lane_out.lstate |= VSW_ATTR_INFO_SENT; 54571ae08745Sheppo 54581ae08745Sheppo DUMP_TAG(attr_msg.tag); 54591ae08745Sheppo 54601ae08745Sheppo vsw_send_msg(ldcp, &attr_msg, sizeof (vnet_attr_msg_t)); 54611ae08745Sheppo 54621ae08745Sheppo D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id); 54631ae08745Sheppo } 54641ae08745Sheppo 54651ae08745Sheppo /* 54661ae08745Sheppo * Create dring info msg (which also results in the creation of 54671ae08745Sheppo * a dring). 54681ae08745Sheppo */ 54691ae08745Sheppo static vio_dring_reg_msg_t * 54701ae08745Sheppo vsw_create_dring_info_pkt(vsw_ldc_t *ldcp) 54711ae08745Sheppo { 54721ae08745Sheppo vio_dring_reg_msg_t *mp; 54731ae08745Sheppo dring_info_t *dp; 54741ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 54751ae08745Sheppo 54761ae08745Sheppo D1(vswp, "vsw_create_dring_info_pkt enter\n"); 54771ae08745Sheppo 54781ae08745Sheppo /* 54791ae08745Sheppo * If we can't create a dring, obviously no point sending 54801ae08745Sheppo * a message. 54811ae08745Sheppo */ 54821ae08745Sheppo if ((dp = vsw_create_dring(ldcp)) == NULL) 54831ae08745Sheppo return (NULL); 54841ae08745Sheppo 54851ae08745Sheppo mp = kmem_zalloc(sizeof (vio_dring_reg_msg_t), KM_SLEEP); 54861ae08745Sheppo 54871ae08745Sheppo mp->tag.vio_msgtype = VIO_TYPE_CTRL; 54881ae08745Sheppo mp->tag.vio_subtype = VIO_SUBTYPE_INFO; 54891ae08745Sheppo mp->tag.vio_subtype_env = VIO_DRING_REG; 54901ae08745Sheppo mp->tag.vio_sid = ldcp->local_session; 54911ae08745Sheppo 54921ae08745Sheppo /* payload */ 54931ae08745Sheppo mp->num_descriptors = dp->num_descriptors; 54941ae08745Sheppo mp->descriptor_size = dp->descriptor_size; 54951ae08745Sheppo mp->options = dp->options; 54961ae08745Sheppo mp->ncookies = dp->ncookies; 54971ae08745Sheppo bcopy(&dp->cookie[0], &mp->cookie[0], sizeof (ldc_mem_cookie_t)); 54981ae08745Sheppo 54991ae08745Sheppo mp->dring_ident = 0; 55001ae08745Sheppo 55011ae08745Sheppo D1(vswp, "vsw_create_dring_info_pkt exit\n"); 55021ae08745Sheppo 55031ae08745Sheppo return (mp); 55041ae08745Sheppo } 55051ae08745Sheppo 55061ae08745Sheppo static void 55071ae08745Sheppo vsw_send_dring_info(vsw_ldc_t *ldcp) 55081ae08745Sheppo { 55091ae08745Sheppo vio_dring_reg_msg_t *dring_msg; 55101ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 55111ae08745Sheppo 55121ae08745Sheppo D1(vswp, "%s: (%ld) enter", __func__, ldcp->ldc_id); 55131ae08745Sheppo 55141ae08745Sheppo dring_msg = vsw_create_dring_info_pkt(ldcp); 55151ae08745Sheppo if (dring_msg == NULL) { 55161ae08745Sheppo cmn_err(CE_WARN, "vsw_send_dring_info: error creating msg"); 55171ae08745Sheppo return; 55181ae08745Sheppo } 55191ae08745Sheppo 55201ae08745Sheppo ldcp->lane_out.lstate |= VSW_DRING_INFO_SENT; 55211ae08745Sheppo 55221ae08745Sheppo DUMP_TAG_PTR((vio_msg_tag_t *)dring_msg); 55231ae08745Sheppo 55241ae08745Sheppo vsw_send_msg(ldcp, dring_msg, 55251ae08745Sheppo sizeof (vio_dring_reg_msg_t)); 55261ae08745Sheppo 55271ae08745Sheppo kmem_free(dring_msg, sizeof (vio_dring_reg_msg_t)); 55281ae08745Sheppo 55291ae08745Sheppo D1(vswp, "%s: (%ld) exit", __func__, ldcp->ldc_id); 55301ae08745Sheppo } 55311ae08745Sheppo 55321ae08745Sheppo static void 55331ae08745Sheppo vsw_send_rdx(vsw_ldc_t *ldcp) 55341ae08745Sheppo { 55351ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 55361ae08745Sheppo vio_rdx_msg_t rdx_msg; 55371ae08745Sheppo 55381ae08745Sheppo D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id); 55391ae08745Sheppo 55401ae08745Sheppo rdx_msg.tag.vio_msgtype = VIO_TYPE_CTRL; 55411ae08745Sheppo rdx_msg.tag.vio_subtype = VIO_SUBTYPE_INFO; 55421ae08745Sheppo rdx_msg.tag.vio_subtype_env = VIO_RDX; 55431ae08745Sheppo rdx_msg.tag.vio_sid = ldcp->local_session; 55441ae08745Sheppo 55451ae08745Sheppo ldcp->lane_out.lstate |= VSW_RDX_INFO_SENT; 55461ae08745Sheppo 55471ae08745Sheppo DUMP_TAG(rdx_msg.tag); 55481ae08745Sheppo 55491ae08745Sheppo vsw_send_msg(ldcp, &rdx_msg, sizeof (vio_rdx_msg_t)); 55501ae08745Sheppo 55511ae08745Sheppo D1(vswp, "%s (%ld) exit", __func__, ldcp->ldc_id); 55521ae08745Sheppo } 55531ae08745Sheppo 55541ae08745Sheppo /* 55551ae08745Sheppo * Generic routine to send message out over ldc channel. 55561ae08745Sheppo */ 55571ae08745Sheppo static void 55581ae08745Sheppo vsw_send_msg(vsw_ldc_t *ldcp, void *msgp, int size) 55591ae08745Sheppo { 55601ae08745Sheppo int rv; 55611ae08745Sheppo size_t msglen = size; 55621ae08745Sheppo vio_msg_tag_t *tag = (vio_msg_tag_t *)msgp; 55631ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 55641ae08745Sheppo 55651ae08745Sheppo D1(vswp, "vsw_send_msg (%lld) enter : sending %d bytes", 55661ae08745Sheppo ldcp->ldc_id, size); 55671ae08745Sheppo 55681ae08745Sheppo D2(vswp, "send_msg: type 0x%llx", tag->vio_msgtype); 55691ae08745Sheppo D2(vswp, "send_msg: stype 0x%llx", tag->vio_subtype); 55701ae08745Sheppo D2(vswp, "send_msg: senv 0x%llx", tag->vio_subtype_env); 55711ae08745Sheppo 55721ae08745Sheppo mutex_enter(&ldcp->ldc_txlock); 55731ae08745Sheppo do { 55741ae08745Sheppo msglen = size; 55751ae08745Sheppo rv = ldc_write(ldcp->ldc_handle, (caddr_t)msgp, &msglen); 55761ae08745Sheppo } while (rv == EWOULDBLOCK && --vsw_wretries > 0); 55771ae08745Sheppo 55781ae08745Sheppo mutex_exit(&ldcp->ldc_txlock); 55791ae08745Sheppo 55801ae08745Sheppo if ((rv != 0) || (msglen != size)) { 55811ae08745Sheppo DERR(vswp, "vsw_send_msg:ldc_write failed: chan(%lld) " 55821ae08745Sheppo "rv(%d) size (%d) msglen(%d)\n", ldcp->ldc_id, 55831ae08745Sheppo rv, size, msglen); 55841ae08745Sheppo } 55851ae08745Sheppo 55861ae08745Sheppo D1(vswp, "vsw_send_msg (%lld) exit : sent %d bytes", 55871ae08745Sheppo ldcp->ldc_id, msglen); 55881ae08745Sheppo } 55891ae08745Sheppo 55901ae08745Sheppo /* 55911ae08745Sheppo * Add an entry into FDB, for the given mac address and port_id. 55921ae08745Sheppo * Returns 0 on success, 1 on failure. 55931ae08745Sheppo * 55941ae08745Sheppo * Lock protecting FDB must be held by calling process. 55951ae08745Sheppo */ 55961ae08745Sheppo static int 55971ae08745Sheppo vsw_add_fdb(vsw_t *vswp, vsw_port_t *port) 55981ae08745Sheppo { 55991ae08745Sheppo uint64_t addr = 0; 56001ae08745Sheppo 56011ae08745Sheppo D1(vswp, "%s: enter", __func__); 56021ae08745Sheppo 56031ae08745Sheppo KEY_HASH(addr, port->p_macaddr); 56041ae08745Sheppo 56051ae08745Sheppo D2(vswp, "%s: key = 0x%llx", __func__, addr); 56061ae08745Sheppo 56071ae08745Sheppo /* 56081ae08745Sheppo * Note: duplicate keys will be rejected by mod_hash. 56091ae08745Sheppo */ 56101ae08745Sheppo if (mod_hash_insert(vswp->fdb, (mod_hash_key_t)addr, 56111ae08745Sheppo (mod_hash_val_t)port) != 0) { 56121ae08745Sheppo DERR(vswp, "%s: unable to add entry into fdb.", __func__); 56131ae08745Sheppo return (1); 56141ae08745Sheppo } 56151ae08745Sheppo 56161ae08745Sheppo D1(vswp, "%s: exit", __func__); 56171ae08745Sheppo return (0); 56181ae08745Sheppo } 56191ae08745Sheppo 56201ae08745Sheppo /* 56211ae08745Sheppo * Remove an entry from FDB. 56221ae08745Sheppo * Returns 0 on success, 1 on failure. 56231ae08745Sheppo */ 56241ae08745Sheppo static int 56251ae08745Sheppo vsw_del_fdb(vsw_t *vswp, vsw_port_t *port) 56261ae08745Sheppo { 56271ae08745Sheppo uint64_t addr = 0; 56281ae08745Sheppo 56291ae08745Sheppo D1(vswp, "%s: enter", __func__); 56301ae08745Sheppo 56311ae08745Sheppo KEY_HASH(addr, port->p_macaddr); 56321ae08745Sheppo 56331ae08745Sheppo D2(vswp, "%s: key = 0x%llx", __func__, addr); 56341ae08745Sheppo 56351ae08745Sheppo (void) mod_hash_destroy(vswp->fdb, (mod_hash_val_t)addr); 56361ae08745Sheppo 56371ae08745Sheppo D1(vswp, "%s: enter", __func__); 56381ae08745Sheppo 56391ae08745Sheppo return (0); 56401ae08745Sheppo } 56411ae08745Sheppo 56421ae08745Sheppo /* 56431ae08745Sheppo * Search fdb for a given mac address. 56441ae08745Sheppo * Returns pointer to the entry if found, else returns NULL. 56451ae08745Sheppo */ 56461ae08745Sheppo static vsw_port_t * 56471ae08745Sheppo vsw_lookup_fdb(vsw_t *vswp, struct ether_header *ehp) 56481ae08745Sheppo { 56491ae08745Sheppo uint64_t key = 0; 56501ae08745Sheppo vsw_port_t *port = NULL; 56511ae08745Sheppo 56521ae08745Sheppo D1(vswp, "%s: enter", __func__); 56531ae08745Sheppo 56541ae08745Sheppo KEY_HASH(key, ehp->ether_dhost); 56551ae08745Sheppo 56561ae08745Sheppo D2(vswp, "%s: key = 0x%llx", __func__, key); 56571ae08745Sheppo 56581ae08745Sheppo if (mod_hash_find(vswp->fdb, (mod_hash_key_t)key, 56591ae08745Sheppo (mod_hash_val_t *)&port) != 0) { 56601ae08745Sheppo return (NULL); 56611ae08745Sheppo } 56621ae08745Sheppo 56631ae08745Sheppo D1(vswp, "%s: exit", __func__); 56641ae08745Sheppo 56651ae08745Sheppo return (port); 56661ae08745Sheppo } 56671ae08745Sheppo 56681ae08745Sheppo /* 56691ae08745Sheppo * Add or remove multicast address(es). 56701ae08745Sheppo * 56711ae08745Sheppo * Returns 0 on success, 1 on failure. 56721ae08745Sheppo */ 56731ae08745Sheppo static int 56741ae08745Sheppo vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port) 56751ae08745Sheppo { 56761ae08745Sheppo mcst_addr_t *mcst_p = NULL; 56771ae08745Sheppo vsw_t *vswp = port->p_vswp; 56781ae08745Sheppo uint64_t addr = 0x0; 56791ae08745Sheppo int i; 56801ae08745Sheppo 56811ae08745Sheppo D1(vswp, "%s: enter", __func__); 56821ae08745Sheppo 56831ae08745Sheppo D2(vswp, "%s: %d addresses", __func__, mcst_pkt->count); 56841ae08745Sheppo 56851ae08745Sheppo for (i = 0; i < mcst_pkt->count; i++) { 56861ae08745Sheppo /* 56871ae08745Sheppo * Convert address into form that can be used 56881ae08745Sheppo * as hash table key. 56891ae08745Sheppo */ 56901ae08745Sheppo KEY_HASH(addr, mcst_pkt->mca[i]); 56911ae08745Sheppo 56921ae08745Sheppo /* 56931ae08745Sheppo * Add or delete the specified address/port combination. 56941ae08745Sheppo */ 56951ae08745Sheppo if (mcst_pkt->set == 0x1) { 56961ae08745Sheppo D3(vswp, "%s: adding multicast address 0x%llx for " 56971ae08745Sheppo "port %ld", __func__, addr, port->p_instance); 56981ae08745Sheppo if (vsw_add_mcst(vswp, VSW_VNETPORT, addr, port) == 0) { 56991ae08745Sheppo /* 57001ae08745Sheppo * Update the list of multicast 57011ae08745Sheppo * addresses contained within the 57021ae08745Sheppo * port structure to include this new 57031ae08745Sheppo * one. 57041ae08745Sheppo */ 57051ae08745Sheppo mcst_p = kmem_alloc(sizeof (mcst_addr_t), 57061ae08745Sheppo KM_NOSLEEP); 57071ae08745Sheppo if (mcst_p == NULL) { 57081ae08745Sheppo DERR(vswp, "%s: unable to alloc mem", 57091ae08745Sheppo __func__); 57101ae08745Sheppo return (1); 57111ae08745Sheppo } 57121ae08745Sheppo 57131ae08745Sheppo mcst_p->nextp = NULL; 57141ae08745Sheppo mcst_p->addr = addr; 57151ae08745Sheppo 57161ae08745Sheppo mutex_enter(&port->mca_lock); 57171ae08745Sheppo mcst_p->nextp = port->mcap; 57181ae08745Sheppo port->mcap = mcst_p; 57191ae08745Sheppo mutex_exit(&port->mca_lock); 57201ae08745Sheppo 57211ae08745Sheppo /* 57221ae08745Sheppo * Program the address into HW. If the addr 57231ae08745Sheppo * has already been programmed then the MAC 57241ae08745Sheppo * just increments a ref counter (which is 57251ae08745Sheppo * used when the address is being deleted) 57261ae08745Sheppo * 57271ae08745Sheppo * Note: 57281ae08745Sheppo * For the moment we dont care if this 57291ae08745Sheppo * succeeds because the card must be in 57301ae08745Sheppo * promics mode. When we have the ability 57311ae08745Sheppo * to program multiple unicst address into 57321ae08745Sheppo * the card then we will need to check this 57331ae08745Sheppo * return value. 57341ae08745Sheppo */ 57351ae08745Sheppo if (vswp->mh != NULL) 57361ae08745Sheppo (void) mac_multicst_add(vswp->mh, 57371ae08745Sheppo (uchar_t *)&mcst_pkt->mca[i]); 57381ae08745Sheppo 57391ae08745Sheppo } else { 57401ae08745Sheppo DERR(vswp, "%s: error adding multicast " 57411ae08745Sheppo "address 0x%llx for port %ld", 57421ae08745Sheppo __func__, addr, port->p_instance); 57431ae08745Sheppo return (1); 57441ae08745Sheppo } 57451ae08745Sheppo } else { 57461ae08745Sheppo /* 57471ae08745Sheppo * Delete an entry from the multicast hash 57481ae08745Sheppo * table and update the address list 57491ae08745Sheppo * appropriately. 57501ae08745Sheppo */ 57511ae08745Sheppo if (vsw_del_mcst(vswp, VSW_VNETPORT, addr, port) == 0) { 57521ae08745Sheppo D3(vswp, "%s: deleting multicast address " 57531ae08745Sheppo "0x%llx for port %ld", __func__, addr, 57541ae08745Sheppo port->p_instance); 57551ae08745Sheppo 57561ae08745Sheppo vsw_del_addr(VSW_VNETPORT, port, addr); 57571ae08745Sheppo 57581ae08745Sheppo /* 57591ae08745Sheppo * Remove the address from HW. The address 57601ae08745Sheppo * will actually only be removed once the ref 57611ae08745Sheppo * count within the MAC layer has dropped to 57621ae08745Sheppo * zero. I.e. we can safely call this fn even 57631ae08745Sheppo * if other ports are interested in this 57641ae08745Sheppo * address. 57651ae08745Sheppo */ 57661ae08745Sheppo if (vswp->mh != NULL) 57671ae08745Sheppo (void) mac_multicst_remove(vswp->mh, 57681ae08745Sheppo (uchar_t *)&mcst_pkt->mca[i]); 57691ae08745Sheppo 57701ae08745Sheppo } else { 57711ae08745Sheppo DERR(vswp, "%s: error deleting multicast " 57721ae08745Sheppo "addr 0x%llx for port %ld", 57731ae08745Sheppo __func__, addr, port->p_instance); 57741ae08745Sheppo return (1); 57751ae08745Sheppo } 57761ae08745Sheppo } 57771ae08745Sheppo } 57781ae08745Sheppo D1(vswp, "%s: exit", __func__); 57791ae08745Sheppo return (0); 57801ae08745Sheppo } 57811ae08745Sheppo 57821ae08745Sheppo /* 57831ae08745Sheppo * Add a new multicast entry. 57841ae08745Sheppo * 57851ae08745Sheppo * Search hash table based on address. If match found then 57861ae08745Sheppo * update associated val (which is chain of ports), otherwise 57871ae08745Sheppo * create new key/val (addr/port) pair and insert into table. 57881ae08745Sheppo */ 57891ae08745Sheppo static int 57901ae08745Sheppo vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) 57911ae08745Sheppo { 57921ae08745Sheppo int dup = 0; 57931ae08745Sheppo int rv = 0; 57941ae08745Sheppo mfdb_ent_t *ment = NULL; 57951ae08745Sheppo mfdb_ent_t *tmp_ent = NULL; 57961ae08745Sheppo mfdb_ent_t *new_ent = NULL; 57971ae08745Sheppo void *tgt = NULL; 57981ae08745Sheppo 57991ae08745Sheppo if (devtype == VSW_VNETPORT) { 58001ae08745Sheppo /* 58011ae08745Sheppo * Being invoked from a vnet. 58021ae08745Sheppo */ 58031ae08745Sheppo ASSERT(arg != NULL); 58041ae08745Sheppo tgt = arg; 58051ae08745Sheppo D2(NULL, "%s: port %d : address 0x%llx", __func__, 58061ae08745Sheppo ((vsw_port_t *)arg)->p_instance, addr); 58071ae08745Sheppo } else { 58081ae08745Sheppo /* 58091ae08745Sheppo * We are being invoked via the m_multicst mac entry 58101ae08745Sheppo * point. 58111ae08745Sheppo */ 58121ae08745Sheppo D2(NULL, "%s: address 0x%llx", __func__, addr); 58131ae08745Sheppo tgt = (void *)vswp; 58141ae08745Sheppo } 58151ae08745Sheppo 58161ae08745Sheppo WRITE_ENTER(&vswp->mfdbrw); 58171ae08745Sheppo if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, 58181ae08745Sheppo (mod_hash_val_t *)&ment) != 0) { 58191ae08745Sheppo 58201ae08745Sheppo /* address not currently in table */ 58211ae08745Sheppo ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); 58221ae08745Sheppo ment->d_addr = (void *)tgt; 58231ae08745Sheppo ment->d_type = devtype; 58241ae08745Sheppo ment->nextp = NULL; 58251ae08745Sheppo 58261ae08745Sheppo if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr, 58271ae08745Sheppo (mod_hash_val_t)ment) != 0) { 58281ae08745Sheppo DERR(vswp, "%s: hash table insertion failed", __func__); 58291ae08745Sheppo kmem_free(ment, sizeof (mfdb_ent_t)); 58301ae08745Sheppo rv = 1; 58311ae08745Sheppo } else { 58321ae08745Sheppo D2(vswp, "%s: added initial entry for 0x%llx to " 58331ae08745Sheppo "table", __func__, addr); 58341ae08745Sheppo } 58351ae08745Sheppo } else { 58361ae08745Sheppo /* 58371ae08745Sheppo * Address in table. Check to see if specified port 58381ae08745Sheppo * is already associated with the address. If not add 58391ae08745Sheppo * it now. 58401ae08745Sheppo */ 58411ae08745Sheppo tmp_ent = ment; 58421ae08745Sheppo while (tmp_ent != NULL) { 58431ae08745Sheppo if (tmp_ent->d_addr == (void *)tgt) { 58441ae08745Sheppo if (devtype == VSW_VNETPORT) { 58451ae08745Sheppo DERR(vswp, "%s: duplicate port entry " 58461ae08745Sheppo "found for portid %ld and key " 58471ae08745Sheppo "0x%llx", __func__, 58481ae08745Sheppo ((vsw_port_t *)arg)->p_instance, 58491ae08745Sheppo addr); 58501ae08745Sheppo } else { 58511ae08745Sheppo DERR(vswp, "%s: duplicate entry found" 58521ae08745Sheppo "for key 0x%llx", 58531ae08745Sheppo __func__, addr); 58541ae08745Sheppo } 58551ae08745Sheppo rv = 1; 58561ae08745Sheppo dup = 1; 58571ae08745Sheppo break; 58581ae08745Sheppo } 58591ae08745Sheppo tmp_ent = tmp_ent->nextp; 58601ae08745Sheppo } 58611ae08745Sheppo 58621ae08745Sheppo /* 58631ae08745Sheppo * Port not on list so add it to end now. 58641ae08745Sheppo */ 58651ae08745Sheppo if (0 == dup) { 58661ae08745Sheppo D2(vswp, "%s: added entry for 0x%llx to table", 58671ae08745Sheppo __func__, addr); 58681ae08745Sheppo new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); 58691ae08745Sheppo new_ent->d_addr = (void *)tgt; 58701ae08745Sheppo new_ent->d_type = devtype; 58711ae08745Sheppo new_ent->nextp = NULL; 58721ae08745Sheppo 58731ae08745Sheppo tmp_ent = ment; 58741ae08745Sheppo while (tmp_ent->nextp != NULL) 58751ae08745Sheppo tmp_ent = tmp_ent->nextp; 58761ae08745Sheppo 58771ae08745Sheppo tmp_ent->nextp = new_ent; 58781ae08745Sheppo } 58791ae08745Sheppo } 58801ae08745Sheppo 58811ae08745Sheppo RW_EXIT(&vswp->mfdbrw); 58821ae08745Sheppo return (rv); 58831ae08745Sheppo } 58841ae08745Sheppo 58851ae08745Sheppo /* 58861ae08745Sheppo * Remove a multicast entry from the hashtable. 58871ae08745Sheppo * 58881ae08745Sheppo * Search hash table based on address. If match found, scan 58891ae08745Sheppo * list of ports associated with address. If specified port 58901ae08745Sheppo * found remove it from list. 58911ae08745Sheppo */ 58921ae08745Sheppo static int 58931ae08745Sheppo vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) 58941ae08745Sheppo { 58951ae08745Sheppo mfdb_ent_t *ment = NULL; 58961ae08745Sheppo mfdb_ent_t *curr_p, *prev_p; 58971ae08745Sheppo void *tgt = NULL; 58981ae08745Sheppo 58991ae08745Sheppo D1(vswp, "%s: enter", __func__); 59001ae08745Sheppo 59011ae08745Sheppo if (devtype == VSW_VNETPORT) { 59021ae08745Sheppo tgt = (vsw_port_t *)arg; 59031ae08745Sheppo D2(vswp, "%s: removing port %d from mFDB for address" 59041ae08745Sheppo " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, 59051ae08745Sheppo addr); 59061ae08745Sheppo } else { 59071ae08745Sheppo D2(vswp, "%s: removing entry", __func__); 59081ae08745Sheppo tgt = (void *)vswp; 59091ae08745Sheppo } 59101ae08745Sheppo 59111ae08745Sheppo WRITE_ENTER(&vswp->mfdbrw); 59121ae08745Sheppo if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, 59131ae08745Sheppo (mod_hash_val_t *)&ment) != 0) { 59141ae08745Sheppo D2(vswp, "%s: address 0x%llx not in table", __func__, addr); 59151ae08745Sheppo RW_EXIT(&vswp->mfdbrw); 59161ae08745Sheppo return (1); 59171ae08745Sheppo } 59181ae08745Sheppo 59191ae08745Sheppo prev_p = curr_p = ment; 59201ae08745Sheppo 59211ae08745Sheppo while (curr_p != NULL) { 59221ae08745Sheppo if (curr_p->d_addr == (void *)tgt) { 59231ae08745Sheppo if (devtype == VSW_VNETPORT) { 59241ae08745Sheppo D2(vswp, "%s: port %d found", __func__, 59251ae08745Sheppo ((vsw_port_t *)tgt)->p_instance); 59261ae08745Sheppo } else { 59271ae08745Sheppo D2(vswp, "%s: instance found", __func__); 59281ae08745Sheppo } 59291ae08745Sheppo 59301ae08745Sheppo if (prev_p == curr_p) { 59311ae08745Sheppo /* 59321ae08745Sheppo * head of list, if no other element is in 59331ae08745Sheppo * list then destroy this entry, otherwise 59341ae08745Sheppo * just replace it with updated value. 59351ae08745Sheppo */ 59361ae08745Sheppo ment = curr_p->nextp; 59371ae08745Sheppo kmem_free(curr_p, sizeof (mfdb_ent_t)); 59381ae08745Sheppo if (ment == NULL) { 59391ae08745Sheppo (void) mod_hash_destroy(vswp->mfdb, 59401ae08745Sheppo (mod_hash_val_t)addr); 59411ae08745Sheppo } else { 59421ae08745Sheppo (void) mod_hash_replace(vswp->mfdb, 59431ae08745Sheppo (mod_hash_key_t)addr, 59441ae08745Sheppo (mod_hash_val_t)ment); 59451ae08745Sheppo } 59461ae08745Sheppo } else { 59471ae08745Sheppo /* 59481ae08745Sheppo * Not head of list, no need to do 59491ae08745Sheppo * replacement, just adjust list pointers. 59501ae08745Sheppo */ 59511ae08745Sheppo prev_p->nextp = curr_p->nextp; 59521ae08745Sheppo kmem_free(curr_p, sizeof (mfdb_ent_t)); 59531ae08745Sheppo } 59541ae08745Sheppo break; 59551ae08745Sheppo } 59561ae08745Sheppo 59571ae08745Sheppo prev_p = curr_p; 59581ae08745Sheppo curr_p = curr_p->nextp; 59591ae08745Sheppo } 59601ae08745Sheppo 59611ae08745Sheppo RW_EXIT(&vswp->mfdbrw); 59621ae08745Sheppo 59631ae08745Sheppo D1(vswp, "%s: exit", __func__); 59641ae08745Sheppo 59651ae08745Sheppo return (0); 59661ae08745Sheppo } 59671ae08745Sheppo 59681ae08745Sheppo /* 59691ae08745Sheppo * Port is being deleted, but has registered an interest in one 59701ae08745Sheppo * or more multicast groups. Using the list of addresses maintained 59711ae08745Sheppo * within the port structure find the appropriate entry in the hash 59721ae08745Sheppo * table and remove this port from the list of interested ports. 59731ae08745Sheppo */ 59741ae08745Sheppo static void 59751ae08745Sheppo vsw_del_mcst_port(vsw_port_t *port) 59761ae08745Sheppo { 59771ae08745Sheppo mcst_addr_t *mcst_p = NULL; 59781ae08745Sheppo vsw_t *vswp = port->p_vswp; 59791ae08745Sheppo 59801ae08745Sheppo D1(vswp, "%s: enter", __func__); 59811ae08745Sheppo 59821ae08745Sheppo mutex_enter(&port->mca_lock); 59831ae08745Sheppo while (port->mcap != NULL) { 59841ae08745Sheppo (void) vsw_del_mcst(vswp, VSW_VNETPORT, 59851ae08745Sheppo port->mcap->addr, port); 59861ae08745Sheppo 59871ae08745Sheppo mcst_p = port->mcap->nextp; 59881ae08745Sheppo kmem_free(port->mcap, sizeof (mcst_addr_t)); 59891ae08745Sheppo port->mcap = mcst_p; 59901ae08745Sheppo } 59911ae08745Sheppo mutex_exit(&port->mca_lock); 59921ae08745Sheppo 59931ae08745Sheppo D1(vswp, "%s: exit", __func__); 59941ae08745Sheppo } 59951ae08745Sheppo 59961ae08745Sheppo /* 59971ae08745Sheppo * This vsw instance is detaching, but has registered an interest in one 59981ae08745Sheppo * or more multicast groups. Using the list of addresses maintained 59991ae08745Sheppo * within the vsw structure find the appropriate entry in the hash 60001ae08745Sheppo * table and remove this instance from the list of interested ports. 60011ae08745Sheppo */ 60021ae08745Sheppo static void 60031ae08745Sheppo vsw_del_mcst_vsw(vsw_t *vswp) 60041ae08745Sheppo { 60051ae08745Sheppo mcst_addr_t *next_p = NULL; 60061ae08745Sheppo 60071ae08745Sheppo D1(vswp, "%s: enter", __func__); 60081ae08745Sheppo 60091ae08745Sheppo mutex_enter(&vswp->mca_lock); 60101ae08745Sheppo 60111ae08745Sheppo while (vswp->mcap != NULL) { 60121ae08745Sheppo DERR(vswp, "%s: deleting addr 0x%llx", 60131ae08745Sheppo __func__, vswp->mcap->addr); 60141ae08745Sheppo (void) vsw_del_mcst(vswp, VSW_LOCALDEV, 60151ae08745Sheppo vswp->mcap->addr, NULL); 60161ae08745Sheppo 60171ae08745Sheppo next_p = vswp->mcap->nextp; 60181ae08745Sheppo kmem_free(vswp->mcap, sizeof (mcst_addr_t)); 60191ae08745Sheppo vswp->mcap = next_p; 60201ae08745Sheppo } 60211ae08745Sheppo 60221ae08745Sheppo vswp->mcap = NULL; 60231ae08745Sheppo mutex_exit(&vswp->mca_lock); 60241ae08745Sheppo 60251ae08745Sheppo D1(vswp, "%s: exit", __func__); 60261ae08745Sheppo } 60271ae08745Sheppo 60281ae08745Sheppo 60291ae08745Sheppo /* 60301ae08745Sheppo * Remove the specified address from the list of address maintained 60311ae08745Sheppo * in this port node. 60321ae08745Sheppo */ 60331ae08745Sheppo static void 60341ae08745Sheppo vsw_del_addr(uint8_t devtype, void *arg, uint64_t addr) 60351ae08745Sheppo { 60361ae08745Sheppo vsw_t *vswp = NULL; 60371ae08745Sheppo vsw_port_t *port = NULL; 60381ae08745Sheppo mcst_addr_t *prev_p = NULL; 60391ae08745Sheppo mcst_addr_t *curr_p = NULL; 60401ae08745Sheppo 60411ae08745Sheppo D1(NULL, "%s: enter : devtype %d : addr 0x%llx", 60421ae08745Sheppo __func__, devtype, addr); 60431ae08745Sheppo 60441ae08745Sheppo if (devtype == VSW_VNETPORT) { 60451ae08745Sheppo port = (vsw_port_t *)arg; 60461ae08745Sheppo mutex_enter(&port->mca_lock); 60471ae08745Sheppo prev_p = curr_p = port->mcap; 60481ae08745Sheppo } else { 60491ae08745Sheppo vswp = (vsw_t *)arg; 60501ae08745Sheppo mutex_enter(&vswp->mca_lock); 60511ae08745Sheppo prev_p = curr_p = vswp->mcap; 60521ae08745Sheppo } 60531ae08745Sheppo 60541ae08745Sheppo while (curr_p != NULL) { 60551ae08745Sheppo if (curr_p->addr == addr) { 60561ae08745Sheppo D2(NULL, "%s: address found", __func__); 60571ae08745Sheppo /* match found */ 60581ae08745Sheppo if (prev_p == curr_p) { 60591ae08745Sheppo /* list head */ 60601ae08745Sheppo if (devtype == VSW_VNETPORT) 60611ae08745Sheppo port->mcap = curr_p->nextp; 60621ae08745Sheppo else 60631ae08745Sheppo vswp->mcap = curr_p->nextp; 60641ae08745Sheppo } else { 60651ae08745Sheppo prev_p->nextp = curr_p->nextp; 60661ae08745Sheppo } 60671ae08745Sheppo kmem_free(curr_p, sizeof (mcst_addr_t)); 60681ae08745Sheppo break; 60691ae08745Sheppo } else { 60701ae08745Sheppo prev_p = curr_p; 60711ae08745Sheppo curr_p = curr_p->nextp; 60721ae08745Sheppo } 60731ae08745Sheppo } 60741ae08745Sheppo 60751ae08745Sheppo if (devtype == VSW_VNETPORT) 60761ae08745Sheppo mutex_exit(&port->mca_lock); 60771ae08745Sheppo else 60781ae08745Sheppo mutex_exit(&vswp->mca_lock); 60791ae08745Sheppo 60801ae08745Sheppo D1(NULL, "%s: exit", __func__); 60811ae08745Sheppo } 60821ae08745Sheppo 60831ae08745Sheppo /* 60841ae08745Sheppo * Creates a descriptor ring (dring) and links it into the 60851ae08745Sheppo * link of outbound drings for this channel. 60861ae08745Sheppo * 60871ae08745Sheppo * Returns NULL if creation failed. 60881ae08745Sheppo */ 60891ae08745Sheppo static dring_info_t * 60901ae08745Sheppo vsw_create_dring(vsw_ldc_t *ldcp) 60911ae08745Sheppo { 60921ae08745Sheppo vsw_private_desc_t *priv_addr = NULL; 60931ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 60941ae08745Sheppo ldc_mem_info_t minfo; 60951ae08745Sheppo dring_info_t *dp, *tp; 60961ae08745Sheppo int i; 60971ae08745Sheppo 60981ae08745Sheppo dp = (dring_info_t *)kmem_zalloc(sizeof (dring_info_t), KM_SLEEP); 60991ae08745Sheppo 61001ae08745Sheppo mutex_init(&dp->dlock, NULL, MUTEX_DRIVER, NULL); 61011ae08745Sheppo 61021ae08745Sheppo /* create public section of ring */ 61031ae08745Sheppo if ((ldc_mem_dring_create(VSW_RING_NUM_EL, 61041ae08745Sheppo VSW_PUB_SIZE, &dp->handle)) != 0) { 61051ae08745Sheppo 61061ae08745Sheppo DERR(vswp, "vsw_create_dring(%lld): ldc dring create " 61071ae08745Sheppo "failed", ldcp->ldc_id); 61081ae08745Sheppo goto create_fail_exit; 61091ae08745Sheppo } 61101ae08745Sheppo 61111ae08745Sheppo ASSERT(dp->handle != NULL); 61121ae08745Sheppo 61131ae08745Sheppo /* 61141ae08745Sheppo * Get the base address of the public section of the ring. 61151ae08745Sheppo */ 61161ae08745Sheppo if ((ldc_mem_dring_info(dp->handle, &minfo)) != 0) { 61171ae08745Sheppo DERR(vswp, "vsw_create_dring(%lld): dring info failed\n", 61181ae08745Sheppo ldcp->ldc_id); 61191ae08745Sheppo goto dring_fail_exit; 61201ae08745Sheppo } else { 61211ae08745Sheppo ASSERT(minfo.vaddr != 0); 61221ae08745Sheppo dp->pub_addr = minfo.vaddr; 61231ae08745Sheppo } 61241ae08745Sheppo 61251ae08745Sheppo dp->num_descriptors = VSW_RING_NUM_EL; 61261ae08745Sheppo dp->descriptor_size = VSW_PUB_SIZE; 61271ae08745Sheppo dp->options = VIO_TX_DRING; 61281ae08745Sheppo dp->ncookies = 1; /* guaranteed by ldc */ 61291ae08745Sheppo 61301ae08745Sheppo /* 61311ae08745Sheppo * create private portion of ring 61321ae08745Sheppo */ 61331ae08745Sheppo dp->priv_addr = (vsw_private_desc_t *)kmem_zalloc( 61341ae08745Sheppo (sizeof (vsw_private_desc_t) * VSW_RING_NUM_EL), KM_SLEEP); 61351ae08745Sheppo 61361ae08745Sheppo if (vsw_setup_ring(ldcp, dp)) { 61371ae08745Sheppo DERR(vswp, "%s: unable to setup ring", __func__); 61381ae08745Sheppo goto dring_fail_exit; 61391ae08745Sheppo } 61401ae08745Sheppo 61411ae08745Sheppo /* haven't used any descriptors yet */ 61421ae08745Sheppo dp->end_idx = 0; 61431ae08745Sheppo 61441ae08745Sheppo /* bind dring to the channel */ 61451ae08745Sheppo if ((ldc_mem_dring_bind(ldcp->ldc_handle, dp->handle, 61461ae08745Sheppo LDC_SHADOW_MAP, LDC_MEM_RW, 61471ae08745Sheppo &dp->cookie[0], &dp->ncookies)) != 0) { 61481ae08745Sheppo DERR(vswp, "vsw_create_dring: unable to bind to channel " 61491ae08745Sheppo "%lld", ldcp->ldc_id); 61501ae08745Sheppo goto dring_fail_exit; 61511ae08745Sheppo } 61521ae08745Sheppo 61531ae08745Sheppo /* 61541ae08745Sheppo * Only ever create rings for outgoing lane. Link it onto 61551ae08745Sheppo * end of list. 61561ae08745Sheppo */ 61571ae08745Sheppo if (ldcp->lane_out.dringp == NULL) { 61581ae08745Sheppo D2(vswp, "vsw_create_dring: adding first outbound ring"); 61591ae08745Sheppo ldcp->lane_out.dringp = dp; 61601ae08745Sheppo } else { 61611ae08745Sheppo tp = ldcp->lane_out.dringp; 61621ae08745Sheppo while (tp->next != NULL) 61631ae08745Sheppo tp = tp->next; 61641ae08745Sheppo 61651ae08745Sheppo tp->next = dp; 61661ae08745Sheppo } 61671ae08745Sheppo 61681ae08745Sheppo return (dp); 61691ae08745Sheppo 61701ae08745Sheppo dring_fail_exit: 61711ae08745Sheppo (void) ldc_mem_dring_destroy(dp->handle); 61721ae08745Sheppo 61731ae08745Sheppo create_fail_exit: 61741ae08745Sheppo if (dp->priv_addr != NULL) { 61751ae08745Sheppo priv_addr = dp->priv_addr; 61761ae08745Sheppo for (i = 0; i < VSW_RING_NUM_EL; i++) { 61771ae08745Sheppo if (priv_addr->memhandle != NULL) 61781ae08745Sheppo (void) ldc_mem_free_handle( 61791ae08745Sheppo priv_addr->memhandle); 61801ae08745Sheppo priv_addr++; 61811ae08745Sheppo } 61821ae08745Sheppo kmem_free(dp->priv_addr, 61831ae08745Sheppo (sizeof (vsw_private_desc_t) * VSW_RING_NUM_EL)); 61841ae08745Sheppo } 61851ae08745Sheppo mutex_destroy(&dp->dlock); 61861ae08745Sheppo 61871ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 61881ae08745Sheppo return (NULL); 61891ae08745Sheppo } 61901ae08745Sheppo 61911ae08745Sheppo /* 61921ae08745Sheppo * Create a ring consisting of just a private portion and link 61931ae08745Sheppo * it into the list of rings for the outbound lane. 61941ae08745Sheppo * 61951ae08745Sheppo * These type of rings are used primarily for temporary data 61961ae08745Sheppo * storage (i.e. as data buffers). 61971ae08745Sheppo */ 61981ae08745Sheppo void 61991ae08745Sheppo vsw_create_privring(vsw_ldc_t *ldcp) 62001ae08745Sheppo { 62011ae08745Sheppo dring_info_t *dp, *tp; 62021ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 62031ae08745Sheppo 62041ae08745Sheppo D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id); 62051ae08745Sheppo 62061ae08745Sheppo dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP); 62071ae08745Sheppo 62081ae08745Sheppo mutex_init(&dp->dlock, NULL, MUTEX_DRIVER, NULL); 62091ae08745Sheppo 62101ae08745Sheppo /* no public section */ 62111ae08745Sheppo dp->pub_addr = NULL; 62121ae08745Sheppo 62131ae08745Sheppo dp->priv_addr = kmem_zalloc((sizeof (vsw_private_desc_t) * 62141ae08745Sheppo VSW_RING_NUM_EL), KM_SLEEP); 62151ae08745Sheppo 62161ae08745Sheppo if (vsw_setup_ring(ldcp, dp)) { 62171ae08745Sheppo DERR(vswp, "%s: setup of ring failed", __func__); 62181ae08745Sheppo kmem_free(dp->priv_addr, 62191ae08745Sheppo (sizeof (vsw_private_desc_t) * VSW_RING_NUM_EL)); 62201ae08745Sheppo mutex_destroy(&dp->dlock); 62211ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 62221ae08745Sheppo return; 62231ae08745Sheppo } 62241ae08745Sheppo 62251ae08745Sheppo /* haven't used any descriptors yet */ 62261ae08745Sheppo dp->end_idx = 0; 62271ae08745Sheppo 62281ae08745Sheppo /* 62291ae08745Sheppo * Only ever create rings for outgoing lane. Link it onto 62301ae08745Sheppo * end of list. 62311ae08745Sheppo */ 62321ae08745Sheppo if (ldcp->lane_out.dringp == NULL) { 62331ae08745Sheppo D2(vswp, "%s: adding first outbound privring", __func__); 62341ae08745Sheppo ldcp->lane_out.dringp = dp; 62351ae08745Sheppo } else { 62361ae08745Sheppo tp = ldcp->lane_out.dringp; 62371ae08745Sheppo while (tp->next != NULL) 62381ae08745Sheppo tp = tp->next; 62391ae08745Sheppo 62401ae08745Sheppo tp->next = dp; 62411ae08745Sheppo } 62421ae08745Sheppo 62431ae08745Sheppo D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id); 62441ae08745Sheppo } 62451ae08745Sheppo 62461ae08745Sheppo /* 62471ae08745Sheppo * Setup the descriptors in the dring. Returns 0 on success, 1 on 62481ae08745Sheppo * failure. 62491ae08745Sheppo */ 62501ae08745Sheppo int 62511ae08745Sheppo vsw_setup_ring(vsw_ldc_t *ldcp, dring_info_t *dp) 62521ae08745Sheppo { 62531ae08745Sheppo vnet_public_desc_t *pub_addr = NULL; 62541ae08745Sheppo vsw_private_desc_t *priv_addr = NULL; 62551ae08745Sheppo vsw_t *vswp = ldcp->ldc_vswp; 62561ae08745Sheppo uint64_t *tmpp; 62571ae08745Sheppo uint64_t offset = 0; 62581ae08745Sheppo uint32_t ncookies = 0; 62591ae08745Sheppo static char *name = "vsw_setup_ring"; 62601ae08745Sheppo int i, j, rv; 62611ae08745Sheppo 62621ae08745Sheppo /* note - public section may be null */ 62631ae08745Sheppo priv_addr = dp->priv_addr; 62641ae08745Sheppo pub_addr = dp->pub_addr; 62651ae08745Sheppo 62661ae08745Sheppo /* 62671ae08745Sheppo * Allocate the region of memory which will be used to hold 62681ae08745Sheppo * the data the descriptors will refer to. 62691ae08745Sheppo */ 62701ae08745Sheppo dp->data_sz = (VSW_RING_NUM_EL * VSW_RING_EL_DATA_SZ); 62711ae08745Sheppo dp->data_addr = kmem_alloc(dp->data_sz, KM_SLEEP); 62721ae08745Sheppo 62731ae08745Sheppo D2(vswp, "%s: allocated %lld bytes at 0x%llx\n", name, 62741ae08745Sheppo dp->data_sz, dp->data_addr); 62751ae08745Sheppo 62761ae08745Sheppo tmpp = (uint64_t *)dp->data_addr; 62771ae08745Sheppo offset = VSW_RING_EL_DATA_SZ / sizeof (tmpp); 62781ae08745Sheppo 62791ae08745Sheppo /* 62801ae08745Sheppo * Initialise some of the private and public (if they exist) 62811ae08745Sheppo * descriptor fields. 62821ae08745Sheppo */ 62831ae08745Sheppo for (i = 0; i < VSW_RING_NUM_EL; i++) { 62841ae08745Sheppo if ((ldc_mem_alloc_handle(ldcp->ldc_handle, 62851ae08745Sheppo &priv_addr->memhandle)) != 0) { 62861ae08745Sheppo DERR(vswp, "%s: alloc mem handle failed", name); 62871ae08745Sheppo goto setup_ring_cleanup; 62881ae08745Sheppo } 62891ae08745Sheppo 62901ae08745Sheppo priv_addr->datap = (void *)tmpp; 62911ae08745Sheppo 62921ae08745Sheppo rv = ldc_mem_bind_handle(priv_addr->memhandle, 62931ae08745Sheppo (caddr_t)priv_addr->datap, VSW_RING_EL_DATA_SZ, 62941ae08745Sheppo LDC_SHADOW_MAP, LDC_MEM_R|LDC_MEM_W, 62951ae08745Sheppo &(priv_addr->memcookie[0]), &ncookies); 62961ae08745Sheppo if (rv != 0) { 62971ae08745Sheppo DERR(vswp, "%s(%lld): ldc_mem_bind_handle failed " 62981ae08745Sheppo "(rv %d)", name, ldcp->ldc_id, rv); 62991ae08745Sheppo goto setup_ring_cleanup; 63001ae08745Sheppo } 63011ae08745Sheppo priv_addr->bound = 1; 63021ae08745Sheppo 63031ae08745Sheppo D2(vswp, "%s: %d: memcookie 0 : addr 0x%llx : size 0x%llx", 63041ae08745Sheppo name, i, priv_addr->memcookie[0].addr, 63051ae08745Sheppo priv_addr->memcookie[0].size); 63061ae08745Sheppo 63071ae08745Sheppo if (ncookies >= (uint32_t)(VSW_MAX_COOKIES + 1)) { 63081ae08745Sheppo DERR(vswp, "%s(%lld) ldc_mem_bind_handle returned " 63091ae08745Sheppo "invalid num of cookies (%d) for size 0x%llx", 63101ae08745Sheppo name, ldcp->ldc_id, ncookies, 63111ae08745Sheppo VSW_RING_EL_DATA_SZ); 63121ae08745Sheppo 63131ae08745Sheppo goto setup_ring_cleanup; 63141ae08745Sheppo } else { 63151ae08745Sheppo for (j = 1; j < ncookies; j++) { 63161ae08745Sheppo rv = ldc_mem_nextcookie(priv_addr->memhandle, 63171ae08745Sheppo &(priv_addr->memcookie[j])); 63181ae08745Sheppo if (rv != 0) { 63191ae08745Sheppo DERR(vswp, "%s: ldc_mem_nextcookie " 63201ae08745Sheppo "failed rv (%d)", name, rv); 63211ae08745Sheppo goto setup_ring_cleanup; 63221ae08745Sheppo } 63231ae08745Sheppo D3(vswp, "%s: memcookie %d : addr 0x%llx : " 63241ae08745Sheppo "size 0x%llx", name, j, 63251ae08745Sheppo priv_addr->memcookie[j].addr, 63261ae08745Sheppo priv_addr->memcookie[j].size); 63271ae08745Sheppo } 63281ae08745Sheppo 63291ae08745Sheppo } 63301ae08745Sheppo priv_addr->ncookies = ncookies; 63311ae08745Sheppo priv_addr->dstate = VIO_DESC_FREE; 63321ae08745Sheppo 63331ae08745Sheppo if (pub_addr != NULL) { 63341ae08745Sheppo 63351ae08745Sheppo /* link pub and private sides */ 63361ae08745Sheppo priv_addr->descp = pub_addr; 63371ae08745Sheppo 63381ae08745Sheppo pub_addr->hdr.dstate = VIO_DESC_FREE; 63391ae08745Sheppo pub_addr++; 63401ae08745Sheppo } 63411ae08745Sheppo 63421ae08745Sheppo /* 63431ae08745Sheppo * move to next element in the dring and the next 63441ae08745Sheppo * position in the data buffer. 63451ae08745Sheppo */ 63461ae08745Sheppo priv_addr++; 63471ae08745Sheppo tmpp += offset; 63481ae08745Sheppo } 63491ae08745Sheppo 63501ae08745Sheppo return (0); 63511ae08745Sheppo 63521ae08745Sheppo setup_ring_cleanup: 63531ae08745Sheppo priv_addr = dp->priv_addr; 63541ae08745Sheppo 63551ae08745Sheppo for (i = 0; i < VSW_RING_NUM_EL; i++) { 63561ae08745Sheppo (void) ldc_mem_unbind_handle(priv_addr->memhandle); 63571ae08745Sheppo (void) ldc_mem_free_handle(priv_addr->memhandle); 63581ae08745Sheppo 63591ae08745Sheppo priv_addr++; 63601ae08745Sheppo } 63611ae08745Sheppo kmem_free(dp->data_addr, dp->data_sz); 63621ae08745Sheppo 63631ae08745Sheppo return (1); 63641ae08745Sheppo } 63651ae08745Sheppo 63661ae08745Sheppo /* 63671ae08745Sheppo * Searches the private section of a ring for a free descriptor, 63681ae08745Sheppo * starting at the location of the last free descriptor found 63691ae08745Sheppo * previously. 63701ae08745Sheppo * 63711ae08745Sheppo * Returns 0 if free descriptor is available, 1 otherwise. 63721ae08745Sheppo * 63731ae08745Sheppo * FUTURE: might need to return contiguous range of descriptors 63741ae08745Sheppo * as dring info msg assumes all will be contiguous. 63751ae08745Sheppo */ 63761ae08745Sheppo static int 63771ae08745Sheppo vsw_dring_find_free_desc(dring_info_t *dringp, 63781ae08745Sheppo vsw_private_desc_t **priv_p, int *idx) 63791ae08745Sheppo { 63801ae08745Sheppo vsw_private_desc_t *addr; 63811ae08745Sheppo uint64_t i; 63821ae08745Sheppo uint64_t j = 0; 63831ae08745Sheppo uint64_t start = dringp->end_idx; 63841ae08745Sheppo int num = VSW_RING_NUM_EL; 63851ae08745Sheppo int ret = 1; 63861ae08745Sheppo 63871ae08745Sheppo D1(NULL, "%s enter\n", __func__); 63881ae08745Sheppo 63891ae08745Sheppo addr = dringp->priv_addr; 63901ae08745Sheppo 63911ae08745Sheppo D2(NULL, "%s: searching ring, dringp 0x%llx : start pos %lld", 63921ae08745Sheppo __func__, dringp, start); 63931ae08745Sheppo 63941ae08745Sheppo for (i = start; j < num; i = (i + 1) % num, j++) { 63951ae08745Sheppo addr = (vsw_private_desc_t *)dringp->priv_addr + i; 63961ae08745Sheppo D2(NULL, "%s: descriptor %lld : dstate 0x%llx\n", 63971ae08745Sheppo __func__, i, addr->dstate); 63981ae08745Sheppo if (addr->dstate == VIO_DESC_FREE) { 63991ae08745Sheppo D2(NULL, "%s: descriptor %lld is available", 64001ae08745Sheppo __func__, i); 64011ae08745Sheppo *priv_p = addr; 64021ae08745Sheppo *idx = i; 64031ae08745Sheppo dringp->end_idx = (i + 1) % num; 64041ae08745Sheppo ret = 0; 64051ae08745Sheppo break; 64061ae08745Sheppo } 64071ae08745Sheppo } 64081ae08745Sheppo 64091ae08745Sheppo /* ring full */ 64101ae08745Sheppo if (ret == 1) { 64111ae08745Sheppo D2(NULL, "%s: no desp free: started at %d", __func__, start); 64121ae08745Sheppo } 64131ae08745Sheppo 64141ae08745Sheppo D1(NULL, "%s: exit\n", __func__); 64151ae08745Sheppo 64161ae08745Sheppo return (ret); 64171ae08745Sheppo } 64181ae08745Sheppo 64191ae08745Sheppo /* 64201ae08745Sheppo * Copy relevant fields from the private descriptor into the 64211ae08745Sheppo * associated public side. 64221ae08745Sheppo */ 64231ae08745Sheppo static void 64241ae08745Sheppo vsw_dring_priv2pub(vsw_private_desc_t *priv) 64251ae08745Sheppo { 64261ae08745Sheppo vnet_public_desc_t *pub; 64271ae08745Sheppo int i; 64281ae08745Sheppo 64291ae08745Sheppo D1(NULL, "vsw_dring_priv2pub enter\n"); 64301ae08745Sheppo 64311ae08745Sheppo pub = priv->descp; 64321ae08745Sheppo 64331ae08745Sheppo pub->ncookies = priv->ncookies; 64341ae08745Sheppo pub->nbytes = priv->datalen; 64351ae08745Sheppo 64361ae08745Sheppo for (i = 0; i < pub->ncookies; i++) { 64371ae08745Sheppo bcopy(&priv->memcookie[i], &pub->memcookie[i], 64381ae08745Sheppo sizeof (ldc_mem_cookie_t)); 64391ae08745Sheppo } 64401ae08745Sheppo 64411ae08745Sheppo pub->hdr.ack = 1; 64421ae08745Sheppo pub->hdr.dstate = VIO_DESC_READY; 64431ae08745Sheppo 64441ae08745Sheppo D1(NULL, "vsw_dring_priv2pub exit"); 64451ae08745Sheppo } 64461ae08745Sheppo 64471ae08745Sheppo /* 64481ae08745Sheppo * Map from a dring identifier to the ring itself. Returns 64491ae08745Sheppo * pointer to ring or NULL if no match found. 64501ae08745Sheppo */ 64511ae08745Sheppo static dring_info_t * 64521ae08745Sheppo vsw_ident2dring(lane_t *lane, uint64_t ident) 64531ae08745Sheppo { 64541ae08745Sheppo dring_info_t *dp = NULL; 64551ae08745Sheppo 64561ae08745Sheppo if ((dp = lane->dringp) == NULL) { 64571ae08745Sheppo return (NULL); 64581ae08745Sheppo } else { 64591ae08745Sheppo if (dp->ident == ident) 64601ae08745Sheppo return (dp); 64611ae08745Sheppo 64621ae08745Sheppo while (dp != NULL) { 64631ae08745Sheppo if (dp->ident == ident) 64641ae08745Sheppo break; 64651ae08745Sheppo dp = dp->next; 64661ae08745Sheppo } 64671ae08745Sheppo } 64681ae08745Sheppo 64691ae08745Sheppo return (dp); 64701ae08745Sheppo } 64711ae08745Sheppo 64721ae08745Sheppo /* 64731ae08745Sheppo * Set the default lane attributes. These are copied into 64741ae08745Sheppo * the attr msg we send to our peer. If they are not acceptable 64751ae08745Sheppo * then (currently) the handshake ends. 64761ae08745Sheppo */ 64771ae08745Sheppo static void 64781ae08745Sheppo vsw_set_lane_attr(vsw_t *vswp, lane_t *lp) 64791ae08745Sheppo { 64801ae08745Sheppo bzero(lp, sizeof (lane_t)); 64811ae08745Sheppo 64821ae08745Sheppo READ_ENTER(&vswp->if_lockrw); 64831ae08745Sheppo ether_copy(&(vswp->if_addr), &(lp->addr)); 64841ae08745Sheppo RW_EXIT(&vswp->if_lockrw); 64851ae08745Sheppo 64861ae08745Sheppo lp->mtu = VSW_MTU; 64871ae08745Sheppo lp->addr_type = ADDR_TYPE_MAC; 64881ae08745Sheppo lp->xfer_mode = VIO_DRING_MODE; 64891ae08745Sheppo lp->ack_freq = 0; /* for shared mode */ 64901ae08745Sheppo lp->seq_num = VNET_ISS; 64911ae08745Sheppo } 64921ae08745Sheppo 64931ae08745Sheppo /* 64941ae08745Sheppo * Verify that the attributes are acceptable. 64951ae08745Sheppo * 64961ae08745Sheppo * FUTURE: If some attributes are not acceptable, change them 64971ae08745Sheppo * our desired values. 64981ae08745Sheppo */ 64991ae08745Sheppo static int 65001ae08745Sheppo vsw_check_attr(vnet_attr_msg_t *pkt, vsw_port_t *port) 65011ae08745Sheppo { 65021ae08745Sheppo int ret = 0; 65031ae08745Sheppo 65041ae08745Sheppo D1(NULL, "vsw_check_attr enter\n"); 65051ae08745Sheppo 65061ae08745Sheppo /* 65071ae08745Sheppo * Note we currently only support in-band descriptors 65081ae08745Sheppo * and descriptor rings, not packet based transfer (VIO_PKT_MODE) 65091ae08745Sheppo */ 65101ae08745Sheppo if ((pkt->xfer_mode != VIO_DESC_MODE) && 65111ae08745Sheppo (pkt->xfer_mode != VIO_DRING_MODE)) { 65121ae08745Sheppo D2(NULL, "vsw_check_attr: unknown mode %x\n", 65131ae08745Sheppo pkt->xfer_mode); 65141ae08745Sheppo ret = 1; 65151ae08745Sheppo } 65161ae08745Sheppo 65171ae08745Sheppo /* Only support MAC addresses at moment. */ 65181ae08745Sheppo if ((pkt->addr_type != ADDR_TYPE_MAC) || (pkt->addr == 0)) { 65191ae08745Sheppo D2(NULL, "vsw_check_attr: invalid addr_type %x, " 65201ae08745Sheppo "or address 0x%llx\n", pkt->addr_type, 65211ae08745Sheppo pkt->addr); 65221ae08745Sheppo ret = 1; 65231ae08745Sheppo } 65241ae08745Sheppo 65251ae08745Sheppo /* 65261ae08745Sheppo * MAC address supplied by device should match that stored 65271ae08745Sheppo * in the vsw-port OBP node. Need to decide what to do if they 65281ae08745Sheppo * don't match, for the moment just warn but don't fail. 65291ae08745Sheppo */ 65301ae08745Sheppo if (bcmp(&pkt->addr, &port->p_macaddr, ETHERADDRL) != 0) { 65311ae08745Sheppo DERR(NULL, "vsw_check_attr: device supplied address " 65321ae08745Sheppo "0x%llx doesn't match node address 0x%llx\n", 65331ae08745Sheppo pkt->addr, port->p_macaddr); 65341ae08745Sheppo } 65351ae08745Sheppo 65361ae08745Sheppo /* 65371ae08745Sheppo * Ack freq only makes sense in pkt mode, in shared 65381ae08745Sheppo * mode the ring descriptors say whether or not to 65391ae08745Sheppo * send back an ACK. 65401ae08745Sheppo */ 65411ae08745Sheppo if ((pkt->xfer_mode == VIO_DRING_MODE) && 65421ae08745Sheppo (pkt->ack_freq > 0)) { 65431ae08745Sheppo D2(NULL, "vsw_check_attr: non zero ack freq " 65441ae08745Sheppo " in SHM mode\n"); 65451ae08745Sheppo ret = 1; 65461ae08745Sheppo } 65471ae08745Sheppo 65481ae08745Sheppo /* 65491ae08745Sheppo * Note: for the moment we only support ETHER 65501ae08745Sheppo * frames. This may change in the future. 65511ae08745Sheppo */ 65521ae08745Sheppo if ((pkt->mtu > VSW_MTU) || (pkt->mtu <= 0)) { 65531ae08745Sheppo D2(NULL, "vsw_check_attr: invalid MTU (0x%llx)\n", 65541ae08745Sheppo pkt->mtu); 65551ae08745Sheppo ret = 1; 65561ae08745Sheppo } 65571ae08745Sheppo 65581ae08745Sheppo D1(NULL, "vsw_check_attr exit\n"); 65591ae08745Sheppo 65601ae08745Sheppo return (ret); 65611ae08745Sheppo } 65621ae08745Sheppo 65631ae08745Sheppo /* 65641ae08745Sheppo * Returns 1 if there is a problem, 0 otherwise. 65651ae08745Sheppo */ 65661ae08745Sheppo static int 65671ae08745Sheppo vsw_check_dring_info(vio_dring_reg_msg_t *pkt) 65681ae08745Sheppo { 65691ae08745Sheppo _NOTE(ARGUNUSED(pkt)) 65701ae08745Sheppo 65711ae08745Sheppo int ret = 0; 65721ae08745Sheppo 65731ae08745Sheppo D1(NULL, "vsw_check_dring_info enter\n"); 65741ae08745Sheppo 65751ae08745Sheppo if ((pkt->num_descriptors == 0) || 65761ae08745Sheppo (pkt->descriptor_size == 0) || 65771ae08745Sheppo (pkt->ncookies != 1)) { 65781ae08745Sheppo DERR(NULL, "vsw_check_dring_info: invalid dring msg"); 65791ae08745Sheppo ret = 1; 65801ae08745Sheppo } 65811ae08745Sheppo 65821ae08745Sheppo D1(NULL, "vsw_check_dring_info exit\n"); 65831ae08745Sheppo 65841ae08745Sheppo return (ret); 65851ae08745Sheppo } 65861ae08745Sheppo 65871ae08745Sheppo /* 65881ae08745Sheppo * Returns 1 if two memory cookies match. Otherwise returns 0. 65891ae08745Sheppo */ 65901ae08745Sheppo static int 65911ae08745Sheppo vsw_mem_cookie_match(ldc_mem_cookie_t *m1, ldc_mem_cookie_t *m2) 65921ae08745Sheppo { 65931ae08745Sheppo if ((m1->addr != m2->addr) || 65941ae08745Sheppo (m2->size != m2->size)) { 65951ae08745Sheppo return (0); 65961ae08745Sheppo } else { 65971ae08745Sheppo return (1); 65981ae08745Sheppo } 65991ae08745Sheppo } 66001ae08745Sheppo 66011ae08745Sheppo /* 66021ae08745Sheppo * Returns 1 if ring described in reg message matches that 66031ae08745Sheppo * described by dring_info structure. Otherwise returns 0. 66041ae08745Sheppo */ 66051ae08745Sheppo static int 66061ae08745Sheppo vsw_dring_match(dring_info_t *dp, vio_dring_reg_msg_t *msg) 66071ae08745Sheppo { 66081ae08745Sheppo if ((msg->descriptor_size != dp->descriptor_size) || 66091ae08745Sheppo (msg->num_descriptors != dp->num_descriptors) || 66101ae08745Sheppo (msg->ncookies != dp->ncookies) || 66111ae08745Sheppo !(vsw_mem_cookie_match(&msg->cookie[0], &dp->cookie[0]))) { 66121ae08745Sheppo return (0); 66131ae08745Sheppo } else { 66141ae08745Sheppo return (1); 66151ae08745Sheppo } 66161ae08745Sheppo 66171ae08745Sheppo } 66181ae08745Sheppo 66191ae08745Sheppo static caddr_t 66201ae08745Sheppo vsw_print_ethaddr(uint8_t *a, char *ebuf) 66211ae08745Sheppo { 66221ae08745Sheppo (void) sprintf(ebuf, "%x:%x:%x:%x:%x:%x", 66231ae08745Sheppo a[0], a[1], a[2], a[3], a[4], a[5]); 66241ae08745Sheppo return (ebuf); 66251ae08745Sheppo } 66261ae08745Sheppo 66271ae08745Sheppo /* 66281ae08745Sheppo * Reset and free all the resources associated with 66291ae08745Sheppo * the channel. 66301ae08745Sheppo */ 66311ae08745Sheppo static void 66321ae08745Sheppo vsw_free_lane_resources(vsw_ldc_t *ldcp, uint64_t dir) 66331ae08745Sheppo { 66341ae08745Sheppo dring_info_t *dp, *dpp; 66351ae08745Sheppo lane_t *lp = NULL; 66361ae08745Sheppo int rv = 0; 66371ae08745Sheppo 66381ae08745Sheppo ASSERT(ldcp != NULL); 66391ae08745Sheppo 66401ae08745Sheppo D1(ldcp->ldc_vswp, "%s (%lld): enter", __func__, ldcp->ldc_id); 66411ae08745Sheppo 66421ae08745Sheppo if (dir == INBOUND) { 66431ae08745Sheppo D2(ldcp->ldc_vswp, "%s: freeing INBOUND lane" 66441ae08745Sheppo " of channel %lld", __func__, ldcp->ldc_id); 66451ae08745Sheppo lp = &ldcp->lane_in; 66461ae08745Sheppo } else { 66471ae08745Sheppo D2(ldcp->ldc_vswp, "%s: freeing OUTBOUND lane" 66481ae08745Sheppo " of channel %lld", __func__, ldcp->ldc_id); 66491ae08745Sheppo lp = &ldcp->lane_out; 66501ae08745Sheppo } 66511ae08745Sheppo 66521ae08745Sheppo lp->lstate = VSW_LANE_INACTIV; 66531ae08745Sheppo lp->seq_num = VNET_ISS; 66541ae08745Sheppo if (lp->dringp) { 66551ae08745Sheppo if (dir == INBOUND) { 66561ae08745Sheppo dp = lp->dringp; 66571ae08745Sheppo while (dp != NULL) { 66581ae08745Sheppo dpp = dp->next; 66591ae08745Sheppo if (dp->handle != NULL) 66601ae08745Sheppo (void) ldc_mem_dring_unmap(dp->handle); 66611ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 66621ae08745Sheppo dp = dpp; 66631ae08745Sheppo } 66641ae08745Sheppo } else { 66651ae08745Sheppo /* 66661ae08745Sheppo * unbind, destroy exported dring, free dring struct 66671ae08745Sheppo */ 66681ae08745Sheppo dp = lp->dringp; 66691ae08745Sheppo rv = vsw_free_ring(dp); 66701ae08745Sheppo } 66711ae08745Sheppo if (rv == 0) { 66721ae08745Sheppo lp->dringp = NULL; 66731ae08745Sheppo } 66741ae08745Sheppo } 66751ae08745Sheppo 66761ae08745Sheppo D1(ldcp->ldc_vswp, "%s (%lld): exit", __func__, ldcp->ldc_id); 66771ae08745Sheppo } 66781ae08745Sheppo 66791ae08745Sheppo /* 66801ae08745Sheppo * Free ring and all associated resources. 66811ae08745Sheppo */ 66821ae08745Sheppo static int 66831ae08745Sheppo vsw_free_ring(dring_info_t *dp) 66841ae08745Sheppo { 66851ae08745Sheppo vsw_private_desc_t *paddr = NULL; 66861ae08745Sheppo dring_info_t *dpp; 66871ae08745Sheppo int i, rv = 1; 66881ae08745Sheppo 66891ae08745Sheppo while (dp != NULL) { 66901ae08745Sheppo mutex_enter(&dp->dlock); 66911ae08745Sheppo dpp = dp->next; 66921ae08745Sheppo if (dp->priv_addr != NULL) { 66931ae08745Sheppo /* 66941ae08745Sheppo * First unbind and free the memory handles 66951ae08745Sheppo * stored in each descriptor within the ring. 66961ae08745Sheppo */ 66971ae08745Sheppo for (i = 0; i < VSW_RING_NUM_EL; i++) { 66981ae08745Sheppo paddr = (vsw_private_desc_t *) 66991ae08745Sheppo dp->priv_addr + i; 67001ae08745Sheppo if (paddr->memhandle != NULL) { 67011ae08745Sheppo if (paddr->bound == 1) { 67021ae08745Sheppo rv = ldc_mem_unbind_handle( 67031ae08745Sheppo paddr->memhandle); 67041ae08745Sheppo 67051ae08745Sheppo if (rv != 0) { 67061ae08745Sheppo DERR(NULL, "error " 67071ae08745Sheppo "unbinding handle for " 67081ae08745Sheppo "ring 0x%llx at pos %d", 67091ae08745Sheppo dp, i); 67101ae08745Sheppo mutex_exit(&dp->dlock); 67111ae08745Sheppo return (rv); 67121ae08745Sheppo } 67131ae08745Sheppo paddr->bound = 0; 67141ae08745Sheppo } 67151ae08745Sheppo 67161ae08745Sheppo rv = ldc_mem_free_handle( 67171ae08745Sheppo paddr->memhandle); 67181ae08745Sheppo if (rv != 0) { 67191ae08745Sheppo DERR(NULL, "error freeing " 67201ae08745Sheppo "handle for ring " 67211ae08745Sheppo "0x%llx at pos %d", 67221ae08745Sheppo dp, i); 67231ae08745Sheppo mutex_exit(&dp->dlock); 67241ae08745Sheppo return (rv); 67251ae08745Sheppo } 67261ae08745Sheppo paddr->memhandle = NULL; 67271ae08745Sheppo } 67281ae08745Sheppo } 67291ae08745Sheppo kmem_free(dp->priv_addr, (sizeof (vsw_private_desc_t) 67301ae08745Sheppo * VSW_RING_NUM_EL)); 67311ae08745Sheppo } 67321ae08745Sheppo 67331ae08745Sheppo /* 67341ae08745Sheppo * Now unbind and destroy the ring itself. 67351ae08745Sheppo */ 67361ae08745Sheppo if (dp->handle != NULL) { 67371ae08745Sheppo (void) ldc_mem_dring_unbind(dp->handle); 67381ae08745Sheppo (void) ldc_mem_dring_destroy(dp->handle); 67391ae08745Sheppo } 67401ae08745Sheppo 67411ae08745Sheppo if (dp->data_addr != NULL) { 67421ae08745Sheppo kmem_free(dp->data_addr, dp->data_sz); 67431ae08745Sheppo } 67441ae08745Sheppo 67451ae08745Sheppo mutex_exit(&dp->dlock); 67461ae08745Sheppo mutex_destroy(&dp->dlock); 67471ae08745Sheppo kmem_free(dp, sizeof (dring_info_t)); 67481ae08745Sheppo 67491ae08745Sheppo dp = dpp; 67501ae08745Sheppo } 67511ae08745Sheppo return (0); 67521ae08745Sheppo } 67531ae08745Sheppo 67541ae08745Sheppo /* 67551ae08745Sheppo * Debugging routines 67561ae08745Sheppo */ 67571ae08745Sheppo static void 67581ae08745Sheppo display_state(void) 67591ae08745Sheppo { 67601ae08745Sheppo vsw_t *vswp; 67611ae08745Sheppo vsw_port_list_t *plist; 67621ae08745Sheppo vsw_port_t *port; 67631ae08745Sheppo vsw_ldc_list_t *ldcl; 67641ae08745Sheppo vsw_ldc_t *ldcp; 67651ae08745Sheppo 67661ae08745Sheppo cmn_err(CE_NOTE, "***** system state *****"); 67671ae08745Sheppo 67681ae08745Sheppo for (vswp = vsw_head; vswp; vswp = vswp->next) { 67691ae08745Sheppo plist = &vswp->plist; 67701ae08745Sheppo READ_ENTER(&plist->lockrw); 67711ae08745Sheppo cmn_err(CE_CONT, "vsw instance %d has %d ports attached\n", 67721ae08745Sheppo vswp->instance, plist->num_ports); 67731ae08745Sheppo 67741ae08745Sheppo for (port = plist->head; port != NULL; port = port->p_next) { 67751ae08745Sheppo ldcl = &port->p_ldclist; 67761ae08745Sheppo cmn_err(CE_CONT, "port %d : %d ldcs attached\n", 67771ae08745Sheppo port->p_instance, ldcl->num_ldcs); 67781ae08745Sheppo READ_ENTER(&ldcl->lockrw); 67791ae08745Sheppo ldcp = ldcl->head; 67801ae08745Sheppo for (; ldcp != NULL; ldcp = ldcp->ldc_next) { 67811ae08745Sheppo cmn_err(CE_CONT, "chan %lu : dev %d : " 67821ae08745Sheppo "status %d : phase %u\n", 67831ae08745Sheppo ldcp->ldc_id, ldcp->dev_class, 67841ae08745Sheppo ldcp->ldc_status, ldcp->hphase); 67851ae08745Sheppo cmn_err(CE_CONT, "chan %lu : lsession %lu : " 67861ae08745Sheppo "psession %lu\n", 67871ae08745Sheppo ldcp->ldc_id, 67881ae08745Sheppo ldcp->local_session, 67891ae08745Sheppo ldcp->peer_session); 67901ae08745Sheppo 67911ae08745Sheppo cmn_err(CE_CONT, "Inbound lane:\n"); 67921ae08745Sheppo display_lane(&ldcp->lane_in); 67931ae08745Sheppo cmn_err(CE_CONT, "Outbound lane:\n"); 67941ae08745Sheppo display_lane(&ldcp->lane_out); 67951ae08745Sheppo } 67961ae08745Sheppo RW_EXIT(&ldcl->lockrw); 67971ae08745Sheppo } 67981ae08745Sheppo RW_EXIT(&plist->lockrw); 67991ae08745Sheppo } 68001ae08745Sheppo cmn_err(CE_NOTE, "***** system state *****"); 68011ae08745Sheppo } 68021ae08745Sheppo 68031ae08745Sheppo static void 68041ae08745Sheppo display_lane(lane_t *lp) 68051ae08745Sheppo { 68061ae08745Sheppo dring_info_t *drp; 68071ae08745Sheppo 68081ae08745Sheppo cmn_err(CE_CONT, "ver 0x%x:0x%x : state %lx : mtu 0x%lx\n", 68091ae08745Sheppo lp->ver_major, lp->ver_minor, lp->lstate, lp->mtu); 68101ae08745Sheppo cmn_err(CE_CONT, "addr_type %d : addr 0x%lx : xmode %d\n", 68111ae08745Sheppo lp->addr_type, lp->addr, lp->xfer_mode); 68121ae08745Sheppo cmn_err(CE_CONT, "dringp 0x%lx\n", (uint64_t)lp->dringp); 68131ae08745Sheppo 68141ae08745Sheppo cmn_err(CE_CONT, "Dring info:\n"); 68151ae08745Sheppo for (drp = lp->dringp; drp != NULL; drp = drp->next) { 68161ae08745Sheppo cmn_err(CE_CONT, "\tnum_desc %u : dsize %u\n", 68171ae08745Sheppo drp->num_descriptors, drp->descriptor_size); 68181ae08745Sheppo cmn_err(CE_CONT, "\thandle 0x%lx\n", drp->handle); 68191ae08745Sheppo cmn_err(CE_CONT, "\tpub_addr 0x%lx : priv_addr 0x%lx\n", 68201ae08745Sheppo (uint64_t)drp->pub_addr, (uint64_t)drp->priv_addr); 68211ae08745Sheppo cmn_err(CE_CONT, "\tident 0x%lx : end_idx %lu\n", 68221ae08745Sheppo drp->ident, drp->end_idx); 68231ae08745Sheppo display_ring(drp); 68241ae08745Sheppo } 68251ae08745Sheppo } 68261ae08745Sheppo 68271ae08745Sheppo static void 68281ae08745Sheppo display_ring(dring_info_t *dringp) 68291ae08745Sheppo { 68301ae08745Sheppo uint64_t i; 68311ae08745Sheppo uint64_t priv_count = 0; 68321ae08745Sheppo uint64_t pub_count = 0; 68331ae08745Sheppo vnet_public_desc_t *pub_addr = NULL; 68341ae08745Sheppo vsw_private_desc_t *priv_addr = NULL; 68351ae08745Sheppo 68361ae08745Sheppo for (i = 0; i < VSW_RING_NUM_EL; i++) { 68371ae08745Sheppo if (dringp->pub_addr != NULL) { 68381ae08745Sheppo pub_addr = (vnet_public_desc_t *)dringp->pub_addr + i; 68391ae08745Sheppo 68401ae08745Sheppo if (pub_addr->hdr.dstate == VIO_DESC_FREE) 68411ae08745Sheppo pub_count++; 68421ae08745Sheppo } 68431ae08745Sheppo 68441ae08745Sheppo if (dringp->priv_addr != NULL) { 68451ae08745Sheppo priv_addr = 68461ae08745Sheppo (vsw_private_desc_t *)dringp->priv_addr + i; 68471ae08745Sheppo 68481ae08745Sheppo if (priv_addr->dstate == VIO_DESC_FREE) 68491ae08745Sheppo priv_count++; 68501ae08745Sheppo } 68511ae08745Sheppo } 68521ae08745Sheppo cmn_err(CE_CONT, "\t%lu elements: %lu priv free: %lu pub free\n", 68531ae08745Sheppo i, priv_count, pub_count); 68541ae08745Sheppo } 68551ae08745Sheppo 68561ae08745Sheppo static void 68571ae08745Sheppo dump_flags(uint64_t state) 68581ae08745Sheppo { 68591ae08745Sheppo int i; 68601ae08745Sheppo 68611ae08745Sheppo typedef struct flag_name { 68621ae08745Sheppo int flag_val; 68631ae08745Sheppo char *flag_name; 68641ae08745Sheppo } flag_name_t; 68651ae08745Sheppo 68661ae08745Sheppo flag_name_t flags[] = { 68671ae08745Sheppo VSW_VER_INFO_SENT, "VSW_VER_INFO_SENT", 68681ae08745Sheppo VSW_VER_INFO_RECV, "VSW_VER_INFO_RECV", 68691ae08745Sheppo VSW_VER_ACK_RECV, "VSW_VER_ACK_RECV", 68701ae08745Sheppo VSW_VER_ACK_SENT, "VSW_VER_ACK_SENT", 68711ae08745Sheppo VSW_VER_NACK_RECV, "VSW_VER_NACK_RECV", 68721ae08745Sheppo VSW_VER_NACK_SENT, "VSW_VER_NACK_SENT", 68731ae08745Sheppo VSW_ATTR_INFO_SENT, "VSW_ATTR_INFO_SENT", 68741ae08745Sheppo VSW_ATTR_INFO_RECV, "VSW_ATTR_INFO_RECV", 68751ae08745Sheppo VSW_ATTR_ACK_SENT, "VSW_ATTR_ACK_SENT", 68761ae08745Sheppo VSW_ATTR_ACK_RECV, "VSW_ATTR_ACK_RECV", 68771ae08745Sheppo VSW_ATTR_NACK_SENT, "VSW_ATTR_NACK_SENT", 68781ae08745Sheppo VSW_ATTR_NACK_RECV, "VSW_ATTR_NACK_RECV", 68791ae08745Sheppo VSW_DRING_INFO_SENT, "VSW_DRING_INFO_SENT", 68801ae08745Sheppo VSW_DRING_INFO_RECV, "VSW_DRING_INFO_RECV", 68811ae08745Sheppo VSW_DRING_ACK_SENT, "VSW_DRING_ACK_SENT", 68821ae08745Sheppo VSW_DRING_ACK_RECV, "VSW_DRING_ACK_RECV", 68831ae08745Sheppo VSW_DRING_NACK_SENT, "VSW_DRING_NACK_SENT", 68841ae08745Sheppo VSW_DRING_NACK_RECV, "VSW_DRING_NACK_RECV", 68851ae08745Sheppo VSW_RDX_INFO_SENT, "VSW_RDX_INFO_SENT", 68861ae08745Sheppo VSW_RDX_INFO_RECV, "VSW_RDX_INFO_RECV", 68871ae08745Sheppo VSW_RDX_ACK_SENT, "VSW_RDX_ACK_SENT", 68881ae08745Sheppo VSW_RDX_ACK_RECV, "VSW_RDX_ACK_RECV", 68891ae08745Sheppo VSW_RDX_NACK_SENT, "VSW_RDX_NACK_SENT", 68901ae08745Sheppo VSW_RDX_NACK_RECV, "VSW_RDX_NACK_RECV", 68911ae08745Sheppo VSW_MCST_INFO_SENT, "VSW_MCST_INFO_SENT", 68921ae08745Sheppo VSW_MCST_INFO_RECV, "VSW_MCST_INFO_RECV", 68931ae08745Sheppo VSW_MCST_ACK_SENT, "VSW_MCST_ACK_SENT", 68941ae08745Sheppo VSW_MCST_ACK_RECV, "VSW_MCST_ACK_RECV", 68951ae08745Sheppo VSW_MCST_NACK_SENT, "VSW_MCST_NACK_SENT", 68961ae08745Sheppo VSW_MCST_NACK_RECV, "VSW_MCST_NACK_RECV", 68971ae08745Sheppo VSW_LANE_ACTIVE, "VSW_LANE_ACTIVE"}; 68981ae08745Sheppo 68991ae08745Sheppo DERR(NULL, "DUMP_FLAGS: %llx\n", state); 69001ae08745Sheppo for (i = 0; i < sizeof (flags)/sizeof (flag_name_t); i++) { 69011ae08745Sheppo if (state & flags[i].flag_val) 69021ae08745Sheppo DERR(NULL, "DUMP_FLAGS %s", flags[i].flag_name); 69031ae08745Sheppo } 69041ae08745Sheppo } 6905