19087SZhong.Wang@Sun.COM /* 29087SZhong.Wang@Sun.COM * CDDL HEADER START 39087SZhong.Wang@Sun.COM * 49087SZhong.Wang@Sun.COM * The contents of this file are subject to the terms of the 59087SZhong.Wang@Sun.COM * Common Development and Distribution License (the "License"). 69087SZhong.Wang@Sun.COM * You may not use this file except in compliance with the License. 79087SZhong.Wang@Sun.COM * 89087SZhong.Wang@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 99087SZhong.Wang@Sun.COM * or http://www.opensolaris.org/os/licensing. 109087SZhong.Wang@Sun.COM * See the License for the specific language governing permissions 119087SZhong.Wang@Sun.COM * and limitations under the License. 129087SZhong.Wang@Sun.COM * 139087SZhong.Wang@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 149087SZhong.Wang@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 159087SZhong.Wang@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 169087SZhong.Wang@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 179087SZhong.Wang@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 189087SZhong.Wang@Sun.COM * 199087SZhong.Wang@Sun.COM * CDDL HEADER END 209087SZhong.Wang@Sun.COM */ 219087SZhong.Wang@Sun.COM /* 229087SZhong.Wang@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 239087SZhong.Wang@Sun.COM * Use is subject to license terms. 249087SZhong.Wang@Sun.COM */ 259087SZhong.Wang@Sun.COM 269087SZhong.Wang@Sun.COM /* 279087SZhong.Wang@Sun.COM * The following notice accompanied the original version of this file: 289087SZhong.Wang@Sun.COM * 299087SZhong.Wang@Sun.COM * BSD LICENSE 309087SZhong.Wang@Sun.COM * 319087SZhong.Wang@Sun.COM * Copyright(c) 2007 Intel Corporation. All rights reserved. 329087SZhong.Wang@Sun.COM * All rights reserved. 339087SZhong.Wang@Sun.COM * 349087SZhong.Wang@Sun.COM * Redistribution and use in source and binary forms, with or without 359087SZhong.Wang@Sun.COM * modification, are permitted provided that the following conditions 369087SZhong.Wang@Sun.COM * are met: 379087SZhong.Wang@Sun.COM * 389087SZhong.Wang@Sun.COM * * Redistributions of source code must retain the above copyright 399087SZhong.Wang@Sun.COM * notice, this list of conditions and the following disclaimer. 409087SZhong.Wang@Sun.COM * * Redistributions in binary form must reproduce the above copyright 419087SZhong.Wang@Sun.COM * notice, this list of conditions and the following disclaimer in 429087SZhong.Wang@Sun.COM * the documentation and/or other materials provided with the 439087SZhong.Wang@Sun.COM * distribution. 449087SZhong.Wang@Sun.COM * * Neither the name of Intel Corporation nor the names of its 459087SZhong.Wang@Sun.COM * contributors may be used to endorse or promote products derived 469087SZhong.Wang@Sun.COM * from this software without specific prior written permission. 479087SZhong.Wang@Sun.COM * 489087SZhong.Wang@Sun.COM * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 499087SZhong.Wang@Sun.COM * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 509087SZhong.Wang@Sun.COM * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 519087SZhong.Wang@Sun.COM * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 529087SZhong.Wang@Sun.COM * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 539087SZhong.Wang@Sun.COM * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 549087SZhong.Wang@Sun.COM * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 559087SZhong.Wang@Sun.COM * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 569087SZhong.Wang@Sun.COM * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 579087SZhong.Wang@Sun.COM * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 589087SZhong.Wang@Sun.COM * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 599087SZhong.Wang@Sun.COM */ 609087SZhong.Wang@Sun.COM 619087SZhong.Wang@Sun.COM /* 629087SZhong.Wang@Sun.COM * Common FCoE interface interacts with MAC and FCoE clients, managing 639087SZhong.Wang@Sun.COM * FCoE ports, doing MAC address discovery/managment, and FC frame 649087SZhong.Wang@Sun.COM * encapsulation/decapsulation 659087SZhong.Wang@Sun.COM */ 669087SZhong.Wang@Sun.COM 679087SZhong.Wang@Sun.COM #include <sys/stat.h> 689087SZhong.Wang@Sun.COM #include <sys/conf.h> 699087SZhong.Wang@Sun.COM #include <sys/file.h> 709087SZhong.Wang@Sun.COM #include <sys/cred.h> 719087SZhong.Wang@Sun.COM 729087SZhong.Wang@Sun.COM #include <sys/ddi.h> 739087SZhong.Wang@Sun.COM #include <sys/sunddi.h> 749087SZhong.Wang@Sun.COM #include <sys/sunndi.h> 759087SZhong.Wang@Sun.COM #include <sys/byteorder.h> 769087SZhong.Wang@Sun.COM #include <sys/atomic.h> 779087SZhong.Wang@Sun.COM #include <sys/sysmacros.h> 789087SZhong.Wang@Sun.COM #include <sys/cmn_err.h> 799087SZhong.Wang@Sun.COM #include <sys/crc32.h> 809087SZhong.Wang@Sun.COM #include <sys/strsubr.h> 819087SZhong.Wang@Sun.COM 829087SZhong.Wang@Sun.COM #include <sys/mac_client.h> 839087SZhong.Wang@Sun.COM 849087SZhong.Wang@Sun.COM /* 859087SZhong.Wang@Sun.COM * FCoE header files 869087SZhong.Wang@Sun.COM */ 879087SZhong.Wang@Sun.COM #include <sys/fcoe/fcoeio.h> 889087SZhong.Wang@Sun.COM #include <sys/fcoe/fcoe_common.h> 899087SZhong.Wang@Sun.COM 909087SZhong.Wang@Sun.COM /* 919087SZhong.Wang@Sun.COM * Driver's own header files 929087SZhong.Wang@Sun.COM */ 939087SZhong.Wang@Sun.COM #include <fcoe.h> 949087SZhong.Wang@Sun.COM #include <fcoe_fc.h> 959087SZhong.Wang@Sun.COM #include <fcoe_eth.h> 969087SZhong.Wang@Sun.COM 979087SZhong.Wang@Sun.COM /* 989087SZhong.Wang@Sun.COM * Function forward declaration 999087SZhong.Wang@Sun.COM */ 1009087SZhong.Wang@Sun.COM static int fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 1019087SZhong.Wang@Sun.COM static int fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 1029087SZhong.Wang@Sun.COM static int fcoe_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip, 1039087SZhong.Wang@Sun.COM ddi_ctl_enum_t op, void *arg, void *result); 1049087SZhong.Wang@Sun.COM static int fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp); 1059087SZhong.Wang@Sun.COM static int fcoe_close(dev_t dev, int flag, int otype, cred_t *credp); 1069087SZhong.Wang@Sun.COM static int fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 1079087SZhong.Wang@Sun.COM cred_t *credp, int *rval); 1089087SZhong.Wang@Sun.COM static int fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 1099087SZhong.Wang@Sun.COM void **ibuf, void **abuf, void **obuf); 1109087SZhong.Wang@Sun.COM static int fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, 1119087SZhong.Wang@Sun.COM void *obuf); 1129087SZhong.Wang@Sun.COM static int fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode); 1139087SZhong.Wang@Sun.COM static int fcoe_attach_init(fcoe_soft_state_t *this_ss); 1149087SZhong.Wang@Sun.COM static int fcoe_detach_uninit(fcoe_soft_state_t *this_ss); 1159087SZhong.Wang@Sun.COM static int fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip); 1169087SZhong.Wang@Sun.COM static int fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip); 1179087SZhong.Wang@Sun.COM static void fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, 1189087SZhong.Wang@Sun.COM int is_pwwn, uint8_t idx); 1199307Skelly.hu@Sun.COM static fcoe_mac_t *fcoe_create_mac_by_id(datalink_id_t linkid); 1209087SZhong.Wang@Sun.COM static int fcoe_cmp_wwn(fcoe_mac_t *checkedmac); 1219087SZhong.Wang@Sun.COM static void fcoe_watchdog(void *arg); 1229087SZhong.Wang@Sun.COM static void fcoe_worker_init(); 1239087SZhong.Wang@Sun.COM static int fcoe_worker_fini(); 1249087SZhong.Wang@Sun.COM static void fcoe_worker_frame(); 1259087SZhong.Wang@Sun.COM static int fcoe_get_port_list(fcoe_port_instance_t *ports, int count); 1269087SZhong.Wang@Sun.COM 1279087SZhong.Wang@Sun.COM /* 1289087SZhong.Wang@Sun.COM * Driver identificaton stuff 1299087SZhong.Wang@Sun.COM */ 1309087SZhong.Wang@Sun.COM static struct cb_ops fcoe_cb_ops = { 1319087SZhong.Wang@Sun.COM fcoe_open, 1329087SZhong.Wang@Sun.COM fcoe_close, 1339087SZhong.Wang@Sun.COM nodev, 1349087SZhong.Wang@Sun.COM nodev, 1359087SZhong.Wang@Sun.COM nodev, 1369087SZhong.Wang@Sun.COM nodev, 1379087SZhong.Wang@Sun.COM nodev, 1389087SZhong.Wang@Sun.COM fcoe_ioctl, 1399087SZhong.Wang@Sun.COM nodev, 1409087SZhong.Wang@Sun.COM nodev, 1419087SZhong.Wang@Sun.COM nodev, 1429087SZhong.Wang@Sun.COM nochpoll, 1439087SZhong.Wang@Sun.COM ddi_prop_op, 1449087SZhong.Wang@Sun.COM 0, 1459087SZhong.Wang@Sun.COM D_MP | D_NEW | D_HOTPLUG, 1469087SZhong.Wang@Sun.COM CB_REV, 1479087SZhong.Wang@Sun.COM nodev, 1489087SZhong.Wang@Sun.COM nodev 1499087SZhong.Wang@Sun.COM }; 1509087SZhong.Wang@Sun.COM 1519087SZhong.Wang@Sun.COM static struct bus_ops fcoe_busops = { 1529087SZhong.Wang@Sun.COM BUSO_REV, 1539087SZhong.Wang@Sun.COM nullbusmap, /* bus_map */ 1549087SZhong.Wang@Sun.COM NULL, /* bus_get_intrspec */ 1559087SZhong.Wang@Sun.COM NULL, /* bus_add_intrspec */ 1569087SZhong.Wang@Sun.COM NULL, /* bus_remove_intrspec */ 1579087SZhong.Wang@Sun.COM i_ddi_map_fault, /* bus_map_fault */ 1589087SZhong.Wang@Sun.COM ddi_dma_map, /* bus_dma_map */ 1599087SZhong.Wang@Sun.COM ddi_dma_allochdl, /* bus_dma_allochdl */ 1609087SZhong.Wang@Sun.COM ddi_dma_freehdl, /* bus_dma_freehdl */ 1619087SZhong.Wang@Sun.COM ddi_dma_bindhdl, /* bus_dma_bindhdl */ 1629087SZhong.Wang@Sun.COM ddi_dma_unbindhdl, /* bus_unbindhdl */ 1639087SZhong.Wang@Sun.COM ddi_dma_flush, /* bus_dma_flush */ 1649087SZhong.Wang@Sun.COM ddi_dma_win, /* bus_dma_win */ 1659087SZhong.Wang@Sun.COM ddi_dma_mctl, /* bus_dma_ctl */ 1669087SZhong.Wang@Sun.COM fcoe_bus_ctl, /* bus_ctl */ 1679087SZhong.Wang@Sun.COM ddi_bus_prop_op, /* bus_prop_op */ 1689087SZhong.Wang@Sun.COM NULL, /* bus_get_eventcookie */ 1699087SZhong.Wang@Sun.COM NULL, /* bus_add_eventcall */ 1709087SZhong.Wang@Sun.COM NULL, /* bus_remove_event */ 1719087SZhong.Wang@Sun.COM NULL, /* bus_post_event */ 1729087SZhong.Wang@Sun.COM NULL, /* bus_intr_ctl */ 1739087SZhong.Wang@Sun.COM NULL, /* bus_config */ 1749087SZhong.Wang@Sun.COM NULL, /* bus_unconfig */ 1759087SZhong.Wang@Sun.COM NULL, /* bus_fm_init */ 1769087SZhong.Wang@Sun.COM NULL, /* bus_fm_fini */ 1779087SZhong.Wang@Sun.COM NULL, /* bus_fm_access_enter */ 1789087SZhong.Wang@Sun.COM NULL, /* bus_fm_access_exit */ 1799087SZhong.Wang@Sun.COM NULL, /* bus_power */ 1809087SZhong.Wang@Sun.COM NULL 1819087SZhong.Wang@Sun.COM }; 1829087SZhong.Wang@Sun.COM 1839087SZhong.Wang@Sun.COM static struct dev_ops fcoe_ops = { 1849087SZhong.Wang@Sun.COM DEVO_REV, 1859087SZhong.Wang@Sun.COM 0, 1869087SZhong.Wang@Sun.COM nodev, 1879087SZhong.Wang@Sun.COM nulldev, 1889087SZhong.Wang@Sun.COM nulldev, 1899087SZhong.Wang@Sun.COM fcoe_attach, 1909087SZhong.Wang@Sun.COM fcoe_detach, 1919087SZhong.Wang@Sun.COM nodev, 1929087SZhong.Wang@Sun.COM &fcoe_cb_ops, 1939087SZhong.Wang@Sun.COM &fcoe_busops, 194*9328SZhong.Wang@Sun.COM ddi_power, 195*9328SZhong.Wang@Sun.COM ddi_quiesce_not_needed 1969087SZhong.Wang@Sun.COM }; 1979087SZhong.Wang@Sun.COM 1989087SZhong.Wang@Sun.COM #define FCOE_VERSION "20090311-1.00" 1999087SZhong.Wang@Sun.COM #define FCOE_NAME "FCoE Transport v" FCOE_VERSION 2009087SZhong.Wang@Sun.COM #define TASKQ_NAME_LEN 32 2019087SZhong.Wang@Sun.COM 2029087SZhong.Wang@Sun.COM static struct modldrv modldrv = { 2039087SZhong.Wang@Sun.COM &mod_driverops, 2049087SZhong.Wang@Sun.COM FCOE_NAME, 2059087SZhong.Wang@Sun.COM &fcoe_ops, 2069087SZhong.Wang@Sun.COM }; 2079087SZhong.Wang@Sun.COM 2089087SZhong.Wang@Sun.COM static struct modlinkage modlinkage = { 2099087SZhong.Wang@Sun.COM MODREV_1, &modldrv, NULL 2109087SZhong.Wang@Sun.COM }; 2119087SZhong.Wang@Sun.COM 2129087SZhong.Wang@Sun.COM /* 2139087SZhong.Wang@Sun.COM * TRACE for all FCoE related modules 2149087SZhong.Wang@Sun.COM */ 2159087SZhong.Wang@Sun.COM static kmutex_t fcoe_trace_buf_lock; 2169087SZhong.Wang@Sun.COM static int fcoe_trace_buf_curndx = 0; 2179087SZhong.Wang@Sun.COM static int fcoe_trace_on = 1; 2189087SZhong.Wang@Sun.COM static caddr_t fcoe_trace_buf = NULL; 2199087SZhong.Wang@Sun.COM static clock_t fcoe_trace_start = 0; 2209087SZhong.Wang@Sun.COM static caddr_t ftb = NULL; 2219087SZhong.Wang@Sun.COM static int fcoe_trace_buf_size = (1 * 1024 * 1024); 2229087SZhong.Wang@Sun.COM 2239087SZhong.Wang@Sun.COM /* 2249087SZhong.Wang@Sun.COM * Driver's global variables 2259087SZhong.Wang@Sun.COM */ 2269087SZhong.Wang@Sun.COM static void *fcoe_state = NULL; 2279087SZhong.Wang@Sun.COM fcoe_soft_state_t *fcoe_global_ss = NULL; 2289087SZhong.Wang@Sun.COM int fcoe_use_ext_log = 1; 2299087SZhong.Wang@Sun.COM 2309087SZhong.Wang@Sun.COM static ddi_taskq_t *fcoe_worker_taskq; 2319087SZhong.Wang@Sun.COM static fcoe_worker_t *fcoe_workers; 2329087SZhong.Wang@Sun.COM static uint32_t fcoe_nworkers_running; 2339087SZhong.Wang@Sun.COM 2349087SZhong.Wang@Sun.COM const char *fcoe_workers_num = "workers-number"; 2359087SZhong.Wang@Sun.COM volatile int fcoe_nworkers; 2369087SZhong.Wang@Sun.COM 2379087SZhong.Wang@Sun.COM /* 2389087SZhong.Wang@Sun.COM * Common loadable module entry points _init, _fini, _info 2399087SZhong.Wang@Sun.COM */ 2409087SZhong.Wang@Sun.COM 2419087SZhong.Wang@Sun.COM int 2429087SZhong.Wang@Sun.COM _init(void) 2439087SZhong.Wang@Sun.COM { 2449087SZhong.Wang@Sun.COM int ret; 2459087SZhong.Wang@Sun.COM 2469087SZhong.Wang@Sun.COM ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0); 2479087SZhong.Wang@Sun.COM if (ret == 0) { 2489087SZhong.Wang@Sun.COM ret = mod_install(&modlinkage); 2499087SZhong.Wang@Sun.COM if (ret != 0) { 2509087SZhong.Wang@Sun.COM ddi_soft_state_fini(&fcoe_state); 2519087SZhong.Wang@Sun.COM } else { 2529087SZhong.Wang@Sun.COM fcoe_trace_start = ddi_get_lbolt(); 2539087SZhong.Wang@Sun.COM ftb = kmem_zalloc(fcoe_trace_buf_size, 2549087SZhong.Wang@Sun.COM KM_SLEEP); 2559087SZhong.Wang@Sun.COM fcoe_trace_buf = ftb; 2569087SZhong.Wang@Sun.COM mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0); 2579087SZhong.Wang@Sun.COM } 2589087SZhong.Wang@Sun.COM } 2599087SZhong.Wang@Sun.COM 2609087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "exit _init with %x", ret); 2619087SZhong.Wang@Sun.COM 2629087SZhong.Wang@Sun.COM return (ret); 2639087SZhong.Wang@Sun.COM } 2649087SZhong.Wang@Sun.COM 2659087SZhong.Wang@Sun.COM int 2669087SZhong.Wang@Sun.COM _fini(void) 2679087SZhong.Wang@Sun.COM { 2689087SZhong.Wang@Sun.COM int ret; 2699087SZhong.Wang@Sun.COM 2709087SZhong.Wang@Sun.COM ret = mod_remove(&modlinkage); 2719087SZhong.Wang@Sun.COM if (ret == 0) { 2729087SZhong.Wang@Sun.COM ddi_soft_state_fini(&fcoe_state); 2739087SZhong.Wang@Sun.COM } 2749087SZhong.Wang@Sun.COM 2759087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "exit _fini with %x", ret); 2769087SZhong.Wang@Sun.COM if (ret == 0) { 2779087SZhong.Wang@Sun.COM kmem_free(fcoe_trace_buf, fcoe_trace_buf_size); 2789087SZhong.Wang@Sun.COM mutex_destroy(&fcoe_trace_buf_lock); 2799087SZhong.Wang@Sun.COM } 2809087SZhong.Wang@Sun.COM 2819087SZhong.Wang@Sun.COM return (ret); 2829087SZhong.Wang@Sun.COM } 2839087SZhong.Wang@Sun.COM 2849087SZhong.Wang@Sun.COM int 2859087SZhong.Wang@Sun.COM _info(struct modinfo *modinfop) 2869087SZhong.Wang@Sun.COM { 2879087SZhong.Wang@Sun.COM return (mod_info(&modlinkage, modinfop)); 2889087SZhong.Wang@Sun.COM } 2899087SZhong.Wang@Sun.COM 2909087SZhong.Wang@Sun.COM /* 2919087SZhong.Wang@Sun.COM * Autoconfiguration entry points: attach, detach, getinfo 2929087SZhong.Wang@Sun.COM */ 2939087SZhong.Wang@Sun.COM 2949087SZhong.Wang@Sun.COM static int 2959087SZhong.Wang@Sun.COM fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2969087SZhong.Wang@Sun.COM { 2979087SZhong.Wang@Sun.COM int ret = DDI_FAILURE; 2989087SZhong.Wang@Sun.COM int fcoe_ret; 2999087SZhong.Wang@Sun.COM int instance; 3009087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss; 3019087SZhong.Wang@Sun.COM 3029087SZhong.Wang@Sun.COM instance = ddi_get_instance(dip); 3039087SZhong.Wang@Sun.COM switch (cmd) { 3049087SZhong.Wang@Sun.COM case DDI_ATTACH: 3059087SZhong.Wang@Sun.COM ret = ddi_soft_state_zalloc(fcoe_state, instance); 3069087SZhong.Wang@Sun.COM if (ret == DDI_FAILURE) { 3079087SZhong.Wang@Sun.COM FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance); 3089087SZhong.Wang@Sun.COM return (ret); 3099087SZhong.Wang@Sun.COM } 3109087SZhong.Wang@Sun.COM 3119087SZhong.Wang@Sun.COM ss = ddi_get_soft_state(fcoe_state, instance); 3129087SZhong.Wang@Sun.COM ss->ss_dip = dip; 3139087SZhong.Wang@Sun.COM 3149087SZhong.Wang@Sun.COM ASSERT(fcoe_global_ss == NULL); 3159087SZhong.Wang@Sun.COM fcoe_global_ss = ss; 3169087SZhong.Wang@Sun.COM fcoe_ret = fcoe_attach_init(ss); 3179087SZhong.Wang@Sun.COM if (fcoe_ret == FCOE_SUCCESS) { 3189087SZhong.Wang@Sun.COM ret = DDI_SUCCESS; 3199087SZhong.Wang@Sun.COM } 3209087SZhong.Wang@Sun.COM 3219087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret); 3229087SZhong.Wang@Sun.COM break; 3239087SZhong.Wang@Sun.COM 3249087SZhong.Wang@Sun.COM case DDI_RESUME: 3259087SZhong.Wang@Sun.COM ret = DDI_SUCCESS; 3269087SZhong.Wang@Sun.COM break; 3279087SZhong.Wang@Sun.COM 3289087SZhong.Wang@Sun.COM default: 3299087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd); 3309087SZhong.Wang@Sun.COM break; 3319087SZhong.Wang@Sun.COM } 3329087SZhong.Wang@Sun.COM 3339087SZhong.Wang@Sun.COM return (ret); 3349087SZhong.Wang@Sun.COM } 3359087SZhong.Wang@Sun.COM 3369087SZhong.Wang@Sun.COM static int 3379087SZhong.Wang@Sun.COM fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3389087SZhong.Wang@Sun.COM { 3399087SZhong.Wang@Sun.COM int ret = DDI_FAILURE; 3409087SZhong.Wang@Sun.COM int fcoe_ret; 3419087SZhong.Wang@Sun.COM int instance; 3429087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss; 3439087SZhong.Wang@Sun.COM 3449087SZhong.Wang@Sun.COM instance = ddi_get_instance(dip); 3459087SZhong.Wang@Sun.COM ss = ddi_get_soft_state(fcoe_state, instance); 3469087SZhong.Wang@Sun.COM if (ss == NULL) { 3479087SZhong.Wang@Sun.COM return (ret); 3489087SZhong.Wang@Sun.COM } 3499087SZhong.Wang@Sun.COM 3509087SZhong.Wang@Sun.COM ASSERT(fcoe_global_ss != NULL); 3519087SZhong.Wang@Sun.COM ASSERT(dip == fcoe_global_ss->ss_dip); 3529087SZhong.Wang@Sun.COM switch (cmd) { 3539087SZhong.Wang@Sun.COM case DDI_DETACH: 3549087SZhong.Wang@Sun.COM fcoe_ret = fcoe_detach_uninit(ss); 3559087SZhong.Wang@Sun.COM if (fcoe_ret == FCOE_SUCCESS) { 3569087SZhong.Wang@Sun.COM ret = DDI_SUCCESS; 3579087SZhong.Wang@Sun.COM fcoe_global_ss = NULL; 3589087SZhong.Wang@Sun.COM } 3599087SZhong.Wang@Sun.COM 3609087SZhong.Wang@Sun.COM break; 3619087SZhong.Wang@Sun.COM 3629087SZhong.Wang@Sun.COM case DDI_SUSPEND: 3639087SZhong.Wang@Sun.COM ret = DDI_SUCCESS; 3649087SZhong.Wang@Sun.COM break; 3659087SZhong.Wang@Sun.COM 3669087SZhong.Wang@Sun.COM default: 3679087SZhong.Wang@Sun.COM FCOE_LOG(0, "unsupported detach cmd-%x", cmd); 3689087SZhong.Wang@Sun.COM break; 3699087SZhong.Wang@Sun.COM } 3709087SZhong.Wang@Sun.COM 3719087SZhong.Wang@Sun.COM return (ret); 3729087SZhong.Wang@Sun.COM } 3739087SZhong.Wang@Sun.COM 3749087SZhong.Wang@Sun.COM /* 3759087SZhong.Wang@Sun.COM * FCA driver's intercepted bus control operations. 3769087SZhong.Wang@Sun.COM */ 3779087SZhong.Wang@Sun.COM static int 3789087SZhong.Wang@Sun.COM fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip, 3799087SZhong.Wang@Sun.COM ddi_ctl_enum_t op, void *clientarg, void *result) 3809087SZhong.Wang@Sun.COM { 3819087SZhong.Wang@Sun.COM int ret; 3829087SZhong.Wang@Sun.COM switch (op) { 3839087SZhong.Wang@Sun.COM case DDI_CTLOPS_REPORTDEV: 3849087SZhong.Wang@Sun.COM case DDI_CTLOPS_IOMIN: 3859087SZhong.Wang@Sun.COM ret = DDI_SUCCESS; 3869087SZhong.Wang@Sun.COM break; 3879087SZhong.Wang@Sun.COM 3889087SZhong.Wang@Sun.COM case DDI_CTLOPS_INITCHILD: 3899087SZhong.Wang@Sun.COM ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg); 3909087SZhong.Wang@Sun.COM break; 3919087SZhong.Wang@Sun.COM 3929087SZhong.Wang@Sun.COM case DDI_CTLOPS_UNINITCHILD: 3939087SZhong.Wang@Sun.COM ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg); 3949087SZhong.Wang@Sun.COM break; 3959087SZhong.Wang@Sun.COM 3969087SZhong.Wang@Sun.COM default: 3979087SZhong.Wang@Sun.COM ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result); 3989087SZhong.Wang@Sun.COM break; 3999087SZhong.Wang@Sun.COM } 4009087SZhong.Wang@Sun.COM 4019087SZhong.Wang@Sun.COM return (ret); 4029087SZhong.Wang@Sun.COM } 4039087SZhong.Wang@Sun.COM 4049087SZhong.Wang@Sun.COM /* 4059087SZhong.Wang@Sun.COM * We need specify the dev address for client driver's instance, or we 4069087SZhong.Wang@Sun.COM * can't online client driver's instance. 4079087SZhong.Wang@Sun.COM */ 4089087SZhong.Wang@Sun.COM /* ARGSUSED */ 4099087SZhong.Wang@Sun.COM static int 4109087SZhong.Wang@Sun.COM fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 4119087SZhong.Wang@Sun.COM { 4129087SZhong.Wang@Sun.COM char name[32]; 4139087SZhong.Wang@Sun.COM static int inicounter = 0; 4149087SZhong.Wang@Sun.COM static int tgtcounter = 0; 4159087SZhong.Wang@Sun.COM int *counter; 4169087SZhong.Wang@Sun.COM 4179087SZhong.Wang@Sun.COM if (strcmp(ddi_driver_name(client_dip), FCOET_DRIVER_NAME) == 0) { 4189087SZhong.Wang@Sun.COM counter = &tgtcounter; 4199087SZhong.Wang@Sun.COM tgtcounter++; 4209087SZhong.Wang@Sun.COM } else { 4219087SZhong.Wang@Sun.COM counter = &inicounter; 4229087SZhong.Wang@Sun.COM inicounter++; 4239087SZhong.Wang@Sun.COM } 4249087SZhong.Wang@Sun.COM 4259087SZhong.Wang@Sun.COM bzero(name, 32); 4269087SZhong.Wang@Sun.COM (void) sprintf((char *)name, "%x,0", *counter); 4279087SZhong.Wang@Sun.COM ddi_set_name_addr(client_dip, name); 4289087SZhong.Wang@Sun.COM 4299087SZhong.Wang@Sun.COM return (DDI_SUCCESS); 4309087SZhong.Wang@Sun.COM } 4319087SZhong.Wang@Sun.COM 4329087SZhong.Wang@Sun.COM /* ARGSUSED */ 4339087SZhong.Wang@Sun.COM static int 4349087SZhong.Wang@Sun.COM fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip) 4359087SZhong.Wang@Sun.COM { 4369087SZhong.Wang@Sun.COM ddi_set_name_addr(client_dip, NULL); 4379087SZhong.Wang@Sun.COM return (DDI_SUCCESS); 4389087SZhong.Wang@Sun.COM } 4399087SZhong.Wang@Sun.COM 4409087SZhong.Wang@Sun.COM /* 4419087SZhong.Wang@Sun.COM * Device access entry points 4429087SZhong.Wang@Sun.COM */ 4439087SZhong.Wang@Sun.COM static int 4449087SZhong.Wang@Sun.COM fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp) 4459087SZhong.Wang@Sun.COM { 4469087SZhong.Wang@Sun.COM int instance; 4479087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss; 4489087SZhong.Wang@Sun.COM 4499087SZhong.Wang@Sun.COM if (otype != OTYP_CHR) { 4509087SZhong.Wang@Sun.COM return (EINVAL); 4519087SZhong.Wang@Sun.COM } 4529087SZhong.Wang@Sun.COM 4539087SZhong.Wang@Sun.COM /* 4549087SZhong.Wang@Sun.COM * Since this is for debugging only, only allow root to issue ioctl now 4559087SZhong.Wang@Sun.COM */ 4569087SZhong.Wang@Sun.COM if (drv_priv(credp) != 0) { 4579087SZhong.Wang@Sun.COM return (EPERM); 4589087SZhong.Wang@Sun.COM } 4599087SZhong.Wang@Sun.COM 4609087SZhong.Wang@Sun.COM instance = (int)getminor(*devp); 4619087SZhong.Wang@Sun.COM ss = ddi_get_soft_state(fcoe_state, instance); 4629087SZhong.Wang@Sun.COM if (ss == NULL) { 4639087SZhong.Wang@Sun.COM return (ENXIO); 4649087SZhong.Wang@Sun.COM } 4659087SZhong.Wang@Sun.COM 4669087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 4679087SZhong.Wang@Sun.COM if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 4689087SZhong.Wang@Sun.COM /* 4699087SZhong.Wang@Sun.COM * It is already open for exclusive access. 4709087SZhong.Wang@Sun.COM * So shut the door on this caller. 4719087SZhong.Wang@Sun.COM */ 4729087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 4739087SZhong.Wang@Sun.COM return (EBUSY); 4749087SZhong.Wang@Sun.COM } 4759087SZhong.Wang@Sun.COM 4769087SZhong.Wang@Sun.COM if (flag & FEXCL) { 4779087SZhong.Wang@Sun.COM if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) { 4789087SZhong.Wang@Sun.COM /* 4799087SZhong.Wang@Sun.COM * Exclusive operation not possible 4809087SZhong.Wang@Sun.COM * as it is already opened 4819087SZhong.Wang@Sun.COM */ 4829087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 4839087SZhong.Wang@Sun.COM return (EBUSY); 4849087SZhong.Wang@Sun.COM } 4859087SZhong.Wang@Sun.COM ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL; 4869087SZhong.Wang@Sun.COM } 4879087SZhong.Wang@Sun.COM 4889087SZhong.Wang@Sun.COM ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN; 4899087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 4909087SZhong.Wang@Sun.COM 4919087SZhong.Wang@Sun.COM return (0); 4929087SZhong.Wang@Sun.COM } 4939087SZhong.Wang@Sun.COM 4949087SZhong.Wang@Sun.COM /* ARGSUSED */ 4959087SZhong.Wang@Sun.COM static int 4969087SZhong.Wang@Sun.COM fcoe_close(dev_t dev, int flag, int otype, cred_t *credp) 4979087SZhong.Wang@Sun.COM { 4989087SZhong.Wang@Sun.COM int instance; 4999087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss; 5009087SZhong.Wang@Sun.COM 5019087SZhong.Wang@Sun.COM if (otype != OTYP_CHR) { 5029087SZhong.Wang@Sun.COM return (EINVAL); 5039087SZhong.Wang@Sun.COM } 5049087SZhong.Wang@Sun.COM 5059087SZhong.Wang@Sun.COM instance = (int)getminor(dev); 5069087SZhong.Wang@Sun.COM ss = ddi_get_soft_state(fcoe_state, instance); 5079087SZhong.Wang@Sun.COM if (ss == NULL) { 5089087SZhong.Wang@Sun.COM return (ENXIO); 5099087SZhong.Wang@Sun.COM } 5109087SZhong.Wang@Sun.COM 5119087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 5129087SZhong.Wang@Sun.COM if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 5139087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 5149087SZhong.Wang@Sun.COM return (ENODEV); 5159087SZhong.Wang@Sun.COM } 5169087SZhong.Wang@Sun.COM 5179087SZhong.Wang@Sun.COM ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK; 5189087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 5199087SZhong.Wang@Sun.COM 5209087SZhong.Wang@Sun.COM return (0); 5219087SZhong.Wang@Sun.COM } 5229087SZhong.Wang@Sun.COM 5239087SZhong.Wang@Sun.COM /* ARGSUSED */ 5249087SZhong.Wang@Sun.COM static int 5259087SZhong.Wang@Sun.COM fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 5269087SZhong.Wang@Sun.COM cred_t *credp, int *rval) 5279087SZhong.Wang@Sun.COM { 5289087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss; 5299087SZhong.Wang@Sun.COM int ret = 0; 5309087SZhong.Wang@Sun.COM 5319087SZhong.Wang@Sun.COM if (drv_priv(credp) != 0) { 5329087SZhong.Wang@Sun.COM return (EPERM); 5339087SZhong.Wang@Sun.COM } 5349087SZhong.Wang@Sun.COM 5359087SZhong.Wang@Sun.COM ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev)); 5369087SZhong.Wang@Sun.COM if (ss == NULL) { 5379087SZhong.Wang@Sun.COM return (ENXIO); 5389087SZhong.Wang@Sun.COM } 5399087SZhong.Wang@Sun.COM 5409087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 5419087SZhong.Wang@Sun.COM if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) { 5429087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 5439087SZhong.Wang@Sun.COM return (ENXIO); 5449087SZhong.Wang@Sun.COM } 5459087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 5469087SZhong.Wang@Sun.COM 5479087SZhong.Wang@Sun.COM switch (cmd) { 5489087SZhong.Wang@Sun.COM case FCOEIO_CMD: 5499087SZhong.Wang@Sun.COM ret = fcoe_iocmd(ss, data, mode); 5509087SZhong.Wang@Sun.COM break; 5519087SZhong.Wang@Sun.COM default: 5529087SZhong.Wang@Sun.COM FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd); 5539087SZhong.Wang@Sun.COM ret = ENOTTY; 5549087SZhong.Wang@Sun.COM break; 5559087SZhong.Wang@Sun.COM } 5569087SZhong.Wang@Sun.COM 5579087SZhong.Wang@Sun.COM return (ret); 5589087SZhong.Wang@Sun.COM } 5599087SZhong.Wang@Sun.COM 5609087SZhong.Wang@Sun.COM static int 5619087SZhong.Wang@Sun.COM fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio, 5629087SZhong.Wang@Sun.COM void **ibuf, void **abuf, void **obuf) 5639087SZhong.Wang@Sun.COM { 5649087SZhong.Wang@Sun.COM int ret = 0; 5659087SZhong.Wang@Sun.COM 5669087SZhong.Wang@Sun.COM *ibuf = NULL; 5679087SZhong.Wang@Sun.COM *abuf = NULL; 5689087SZhong.Wang@Sun.COM *obuf = NULL; 5699087SZhong.Wang@Sun.COM *fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP); 5709087SZhong.Wang@Sun.COM if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) { 5719087SZhong.Wang@Sun.COM ret = EFAULT; 5729087SZhong.Wang@Sun.COM goto copyin_iocdata_fail; 5739087SZhong.Wang@Sun.COM } 5749087SZhong.Wang@Sun.COM 5759087SZhong.Wang@Sun.COM if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN || 5769087SZhong.Wang@Sun.COM (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN || 5779087SZhong.Wang@Sun.COM (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) { 5789087SZhong.Wang@Sun.COM ret = EFAULT; 5799087SZhong.Wang@Sun.COM goto copyin_iocdata_fail; 5809087SZhong.Wang@Sun.COM } 5819087SZhong.Wang@Sun.COM 5829087SZhong.Wang@Sun.COM if ((*fcoeio)->fcoeio_ilen) { 5839087SZhong.Wang@Sun.COM *ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP); 5849087SZhong.Wang@Sun.COM if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf, 5859087SZhong.Wang@Sun.COM *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) { 5869087SZhong.Wang@Sun.COM ret = EFAULT; 5879087SZhong.Wang@Sun.COM goto copyin_iocdata_fail; 5889087SZhong.Wang@Sun.COM } 5899087SZhong.Wang@Sun.COM } 5909087SZhong.Wang@Sun.COM 5919087SZhong.Wang@Sun.COM if ((*fcoeio)->fcoeio_alen) { 5929087SZhong.Wang@Sun.COM *abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP); 5939087SZhong.Wang@Sun.COM if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf, 5949087SZhong.Wang@Sun.COM *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) { 5959087SZhong.Wang@Sun.COM ret = EFAULT; 5969087SZhong.Wang@Sun.COM goto copyin_iocdata_fail; 5979087SZhong.Wang@Sun.COM } 5989087SZhong.Wang@Sun.COM } 5999087SZhong.Wang@Sun.COM 6009087SZhong.Wang@Sun.COM if ((*fcoeio)->fcoeio_olen) { 6019087SZhong.Wang@Sun.COM *obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP); 6029087SZhong.Wang@Sun.COM } 6039087SZhong.Wang@Sun.COM return (ret); 6049087SZhong.Wang@Sun.COM 6059087SZhong.Wang@Sun.COM copyin_iocdata_fail: 6069087SZhong.Wang@Sun.COM if (*abuf) { 6079087SZhong.Wang@Sun.COM kmem_free(*abuf, (*fcoeio)->fcoeio_alen); 6089087SZhong.Wang@Sun.COM *abuf = NULL; 6099087SZhong.Wang@Sun.COM } 6109087SZhong.Wang@Sun.COM 6119087SZhong.Wang@Sun.COM if (*ibuf) { 6129087SZhong.Wang@Sun.COM kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen); 6139087SZhong.Wang@Sun.COM *ibuf = NULL; 6149087SZhong.Wang@Sun.COM } 6159087SZhong.Wang@Sun.COM 6169087SZhong.Wang@Sun.COM kmem_free(*fcoeio, sizeof (fcoeio_t)); 6179087SZhong.Wang@Sun.COM return (ret); 6189087SZhong.Wang@Sun.COM } 6199087SZhong.Wang@Sun.COM 6209087SZhong.Wang@Sun.COM static int 6219087SZhong.Wang@Sun.COM fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf) 6229087SZhong.Wang@Sun.COM { 6239087SZhong.Wang@Sun.COM if (fcoeio->fcoeio_olen) { 6249087SZhong.Wang@Sun.COM if (ddi_copyout(obuf, 6259087SZhong.Wang@Sun.COM (void *)(unsigned long)fcoeio->fcoeio_obuf, 6269087SZhong.Wang@Sun.COM fcoeio->fcoeio_olen, mode) != 0) { 6279087SZhong.Wang@Sun.COM return (EFAULT); 6289087SZhong.Wang@Sun.COM } 6299087SZhong.Wang@Sun.COM } 6309087SZhong.Wang@Sun.COM 6319087SZhong.Wang@Sun.COM if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) { 6329087SZhong.Wang@Sun.COM return (EFAULT); 6339087SZhong.Wang@Sun.COM } 6349087SZhong.Wang@Sun.COM return (0); 6359087SZhong.Wang@Sun.COM } 6369087SZhong.Wang@Sun.COM 6379087SZhong.Wang@Sun.COM static int 6389087SZhong.Wang@Sun.COM fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode) 6399087SZhong.Wang@Sun.COM { 6409087SZhong.Wang@Sun.COM int ret; 6419087SZhong.Wang@Sun.COM fcoe_mac_t *fcoe_mac; 6429087SZhong.Wang@Sun.COM void *ibuf = NULL; 6439087SZhong.Wang@Sun.COM void *obuf = NULL; 6449087SZhong.Wang@Sun.COM void *abuf = NULL; 6459087SZhong.Wang@Sun.COM fcoeio_t *fcoeio; 6469087SZhong.Wang@Sun.COM 6479087SZhong.Wang@Sun.COM ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf); 6489087SZhong.Wang@Sun.COM if (ret != 0) { 6499087SZhong.Wang@Sun.COM goto fcoeiocmd_release_buf; 6509087SZhong.Wang@Sun.COM } 6519087SZhong.Wang@Sun.COM 6529087SZhong.Wang@Sun.COM /* 6539087SZhong.Wang@Sun.COM * If an exclusive open was demanded during open, ensure that 6549087SZhong.Wang@Sun.COM * only one thread can execute an ioctl at a time 6559087SZhong.Wang@Sun.COM */ 6569087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 6579087SZhong.Wang@Sun.COM if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) { 6589087SZhong.Wang@Sun.COM if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) { 6599087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 6609087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_BUSY; 6619087SZhong.Wang@Sun.COM ret = EBUSY; 6629087SZhong.Wang@Sun.COM goto fcoeiocmd_release_buf; 6639087SZhong.Wang@Sun.COM } 6649087SZhong.Wang@Sun.COM ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY; 6659087SZhong.Wang@Sun.COM } 6669087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 6679087SZhong.Wang@Sun.COM 6689087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = 0; 6699087SZhong.Wang@Sun.COM 6709087SZhong.Wang@Sun.COM switch (fcoeio->fcoeio_cmd) { 6719087SZhong.Wang@Sun.COM case FCOEIO_CREATE_FCOE_PORT: { 6729087SZhong.Wang@Sun.COM fcoeio_create_port_param_t *param = 6739087SZhong.Wang@Sun.COM (fcoeio_create_port_param_t *)ibuf; 6749087SZhong.Wang@Sun.COM int cmpwwn = 0; 6759087SZhong.Wang@Sun.COM fcoe_port_t *eport; 6769087SZhong.Wang@Sun.COM 6779087SZhong.Wang@Sun.COM if (fcoeio->fcoeio_ilen != 6789087SZhong.Wang@Sun.COM sizeof (fcoeio_create_port_param_t) || 6799087SZhong.Wang@Sun.COM fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 6809087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 6819087SZhong.Wang@Sun.COM ret = EINVAL; 6829087SZhong.Wang@Sun.COM break; 6839087SZhong.Wang@Sun.COM } 6849087SZhong.Wang@Sun.COM 6859087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 6869307Skelly.hu@Sun.COM fcoe_mac = fcoe_create_mac_by_id(param->fcp_mac_linkid); 6879087SZhong.Wang@Sun.COM if (fcoe_mac == NULL) { 6889087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 6899087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC; 6909087SZhong.Wang@Sun.COM ret = EIO; 6919087SZhong.Wang@Sun.COM break; 6929087SZhong.Wang@Sun.COM } 6939087SZhong.Wang@Sun.COM 6949087SZhong.Wang@Sun.COM if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) { 6959087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 6969087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_ALREADY; 6979087SZhong.Wang@Sun.COM ret = EALREADY; 6989087SZhong.Wang@Sun.COM break; 6999087SZhong.Wang@Sun.COM } else { 7009087SZhong.Wang@Sun.COM ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc, 7019087SZhong.Wang@Sun.COM &fcoeio->fcoeio_status); 7029087SZhong.Wang@Sun.COM if (ret != 0) { 7039087SZhong.Wang@Sun.COM fcoe_destroy_mac(fcoe_mac); 7049087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 7059087SZhong.Wang@Sun.COM if (fcoeio->fcoeio_status == 0) { 7069087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = 7079087SZhong.Wang@Sun.COM FCOEIOE_OPEN_MAC; 7089087SZhong.Wang@Sun.COM } 7099087SZhong.Wang@Sun.COM ret = EIO; 7109087SZhong.Wang@Sun.COM break; 7119087SZhong.Wang@Sun.COM } else { 7129087SZhong.Wang@Sun.COM fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED; 7139087SZhong.Wang@Sun.COM } 7149087SZhong.Wang@Sun.COM } 7159087SZhong.Wang@Sun.COM 7169087SZhong.Wang@Sun.COM /* 7179087SZhong.Wang@Sun.COM * Provide PWWN and NWWN based on mac address 7189087SZhong.Wang@Sun.COM */ 7199087SZhong.Wang@Sun.COM eport = &fcoe_mac->fm_eport; 7209087SZhong.Wang@Sun.COM if (!param->fcp_pwwn_provided) { 7219087SZhong.Wang@Sun.COM fcoe_init_wwn_from_mac(eport->eport_portwwn, 7229087SZhong.Wang@Sun.COM fcoe_mac->fm_current_addr, 1, 0); 7239087SZhong.Wang@Sun.COM } else { 7249087SZhong.Wang@Sun.COM (void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8); 7259087SZhong.Wang@Sun.COM } 7269087SZhong.Wang@Sun.COM 7279087SZhong.Wang@Sun.COM if (!param->fcp_nwwn_provided) { 7289087SZhong.Wang@Sun.COM fcoe_init_wwn_from_mac(eport->eport_nodewwn, 7299087SZhong.Wang@Sun.COM fcoe_mac->fm_current_addr, 0, 0); 7309087SZhong.Wang@Sun.COM } else { 7319087SZhong.Wang@Sun.COM (void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8); 7329087SZhong.Wang@Sun.COM } 7339087SZhong.Wang@Sun.COM 7349087SZhong.Wang@Sun.COM cmpwwn = fcoe_cmp_wwn(fcoe_mac); 7359087SZhong.Wang@Sun.COM 7369087SZhong.Wang@Sun.COM if (cmpwwn != 0) { 7379087SZhong.Wang@Sun.COM if (cmpwwn == 1) { 7389087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED; 7399087SZhong.Wang@Sun.COM } else if (cmpwwn == -1) { 7409087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED; 7419087SZhong.Wang@Sun.COM } 7429087SZhong.Wang@Sun.COM (void) fcoe_close_mac(fcoe_mac); 7439087SZhong.Wang@Sun.COM fcoe_destroy_mac(fcoe_mac); 7449087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 7459087SZhong.Wang@Sun.COM ret = ENOTUNIQ; 7469087SZhong.Wang@Sun.COM break; 7479087SZhong.Wang@Sun.COM } 7489087SZhong.Wang@Sun.COM 7499087SZhong.Wang@Sun.COM if (ret == 0) { 7509087SZhong.Wang@Sun.COM ret = fcoe_create_port(ss->ss_dip, 7519087SZhong.Wang@Sun.COM fcoe_mac, 7529087SZhong.Wang@Sun.COM (param->fcp_port_type == FCOE_CLIENT_TARGET)); 7539087SZhong.Wang@Sun.COM if (ret != 0) { 7549087SZhong.Wang@Sun.COM (void) fcoe_close_mac(fcoe_mac); 7559087SZhong.Wang@Sun.COM fcoe_destroy_mac(fcoe_mac); 7569087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT; 7579087SZhong.Wang@Sun.COM ret = EIO; 7589087SZhong.Wang@Sun.COM } 7599087SZhong.Wang@Sun.COM } 7609087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 7619087SZhong.Wang@Sun.COM 7629087SZhong.Wang@Sun.COM break; 7639087SZhong.Wang@Sun.COM } 7649087SZhong.Wang@Sun.COM 7659087SZhong.Wang@Sun.COM case FCOEIO_DELETE_FCOE_PORT: { 7669307Skelly.hu@Sun.COM fcoeio_delete_port_param_t *del_port_param = 7679307Skelly.hu@Sun.COM (fcoeio_delete_port_param_t *)ibuf; 7689087SZhong.Wang@Sun.COM 7699307Skelly.hu@Sun.COM if (fcoeio->fcoeio_ilen < sizeof (fcoeio_delete_port_param_t) || 7709087SZhong.Wang@Sun.COM fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) { 7719087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 7729087SZhong.Wang@Sun.COM ret = EINVAL; 7739087SZhong.Wang@Sun.COM break; 7749087SZhong.Wang@Sun.COM } 7759087SZhong.Wang@Sun.COM 7769087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 7779307Skelly.hu@Sun.COM ret = fcoe_delete_port(ss->ss_dip, fcoeio, 7789307Skelly.hu@Sun.COM del_port_param->fdp_mac_linkid); 7799087SZhong.Wang@Sun.COM if (ret != 0) { 7809087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", 7819087SZhong.Wang@Sun.COM "fcoe_delete_port failed: %d", ret); 7829087SZhong.Wang@Sun.COM } 7839087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 7849087SZhong.Wang@Sun.COM break; 7859087SZhong.Wang@Sun.COM } 7869087SZhong.Wang@Sun.COM 7879087SZhong.Wang@Sun.COM case FCOEIO_GET_FCOE_PORT_LIST: { 7889087SZhong.Wang@Sun.COM fcoe_port_list_t *list = (fcoe_port_list_t *)obuf; 7899087SZhong.Wang@Sun.COM int count; 7909087SZhong.Wang@Sun.COM 7919087SZhong.Wang@Sun.COM if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ || 7929087SZhong.Wang@Sun.COM fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) { 7939087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG; 7949087SZhong.Wang@Sun.COM ret = EINVAL; 7959087SZhong.Wang@Sun.COM break; 7969087SZhong.Wang@Sun.COM } 7979087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_ioctl_mutex); 7989087SZhong.Wang@Sun.COM 7999087SZhong.Wang@Sun.COM list->numPorts = 1 + (fcoeio->fcoeio_olen - 8009087SZhong.Wang@Sun.COM sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t); 8019087SZhong.Wang@Sun.COM 8029087SZhong.Wang@Sun.COM count = fcoe_get_port_list(list->ports, list->numPorts); 8039087SZhong.Wang@Sun.COM 8049087SZhong.Wang@Sun.COM if (count > list->numPorts) { 8059087SZhong.Wang@Sun.COM fcoeio->fcoeio_status = FCOEIOE_MORE_DATA; 8069087SZhong.Wang@Sun.COM ret = ENOSPC; 8079087SZhong.Wang@Sun.COM } 8089087SZhong.Wang@Sun.COM list->numPorts = count; 8099087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_ioctl_mutex); 8109087SZhong.Wang@Sun.COM 8119087SZhong.Wang@Sun.COM break; 8129087SZhong.Wang@Sun.COM 8139087SZhong.Wang@Sun.COM } 8149087SZhong.Wang@Sun.COM 8159087SZhong.Wang@Sun.COM default: 8169087SZhong.Wang@Sun.COM return (ENOTTY); 8179087SZhong.Wang@Sun.COM } 8189087SZhong.Wang@Sun.COM 8199087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "fcoe_ioctl returned %d, fcoeio_status = %d", 8209087SZhong.Wang@Sun.COM ret, fcoeio->fcoeio_status); 8219087SZhong.Wang@Sun.COM 8229087SZhong.Wang@Sun.COM fcoeiocmd_release_buf: 8239087SZhong.Wang@Sun.COM if (ret == 0) { 8249087SZhong.Wang@Sun.COM ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 8259087SZhong.Wang@Sun.COM } else if (fcoeio->fcoeio_status) { 8269087SZhong.Wang@Sun.COM (void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf); 8279087SZhong.Wang@Sun.COM } 8289087SZhong.Wang@Sun.COM 8299087SZhong.Wang@Sun.COM if (obuf != NULL) { 8309087SZhong.Wang@Sun.COM kmem_free(obuf, fcoeio->fcoeio_olen); 8319087SZhong.Wang@Sun.COM obuf = NULL; 8329087SZhong.Wang@Sun.COM } 8339087SZhong.Wang@Sun.COM if (abuf != NULL) { 8349087SZhong.Wang@Sun.COM kmem_free(abuf, fcoeio->fcoeio_alen); 8359087SZhong.Wang@Sun.COM abuf = NULL; 8369087SZhong.Wang@Sun.COM } 8379087SZhong.Wang@Sun.COM 8389087SZhong.Wang@Sun.COM if (ibuf != NULL) { 8399087SZhong.Wang@Sun.COM kmem_free(ibuf, fcoeio->fcoeio_ilen); 8409087SZhong.Wang@Sun.COM ibuf = NULL; 8419087SZhong.Wang@Sun.COM } 8429087SZhong.Wang@Sun.COM kmem_free(fcoeio, sizeof (fcoeio_t)); 8439087SZhong.Wang@Sun.COM 8449087SZhong.Wang@Sun.COM return (ret); 8459087SZhong.Wang@Sun.COM } 8469087SZhong.Wang@Sun.COM 8479087SZhong.Wang@Sun.COM /* 8489087SZhong.Wang@Sun.COM * Finish final initialization 8499087SZhong.Wang@Sun.COM */ 8509087SZhong.Wang@Sun.COM static int 8519087SZhong.Wang@Sun.COM fcoe_attach_init(fcoe_soft_state_t *ss) 8529087SZhong.Wang@Sun.COM { 8539087SZhong.Wang@Sun.COM char taskq_name[TASKQ_NAME_LEN]; 8549087SZhong.Wang@Sun.COM 8559087SZhong.Wang@Sun.COM if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR, 8569087SZhong.Wang@Sun.COM ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) { 8579087SZhong.Wang@Sun.COM FCOE_LOG("FCOE", "ddi_create_minor_node failed"); 8589087SZhong.Wang@Sun.COM return (FCOE_FAILURE); 8599087SZhong.Wang@Sun.COM } 8609087SZhong.Wang@Sun.COM 8619087SZhong.Wang@Sun.COM /* 8629087SZhong.Wang@Sun.COM * watchdog responsible for release frame and dispatch events 8639087SZhong.Wang@Sun.COM */ 8649087SZhong.Wang@Sun.COM (void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac"); 8659087SZhong.Wang@Sun.COM taskq_name[TASKQ_NAME_LEN - 1] = 0; 8669087SZhong.Wang@Sun.COM if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL, 8679087SZhong.Wang@Sun.COM taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) { 8689087SZhong.Wang@Sun.COM return (FCOE_FAILURE); 8699087SZhong.Wang@Sun.COM } 8709087SZhong.Wang@Sun.COM 8719087SZhong.Wang@Sun.COM ss->ss_ioctl_flags = 0; 8729087SZhong.Wang@Sun.COM mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL); 8739087SZhong.Wang@Sun.COM list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t), 8749087SZhong.Wang@Sun.COM offsetof(fcoe_mac_t, fm_ss_node)); 8759087SZhong.Wang@Sun.COM list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t), 8769087SZhong.Wang@Sun.COM offsetof(fcoe_i_frame_t, fmi_pending_node)); 8779087SZhong.Wang@Sun.COM 8789087SZhong.Wang@Sun.COM mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0); 8799087SZhong.Wang@Sun.COM cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL); 8809087SZhong.Wang@Sun.COM ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG; 8819087SZhong.Wang@Sun.COM (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq, 8829087SZhong.Wang@Sun.COM fcoe_watchdog, ss, DDI_SLEEP); 8839087SZhong.Wang@Sun.COM while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) { 8849087SZhong.Wang@Sun.COM delay(10); 8859087SZhong.Wang@Sun.COM } 8869087SZhong.Wang@Sun.COM fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip, 8879087SZhong.Wang@Sun.COM DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4); 8889087SZhong.Wang@Sun.COM if (fcoe_nworkers < 1) { 8899087SZhong.Wang@Sun.COM fcoe_nworkers = 4; 8909087SZhong.Wang@Sun.COM } 8919087SZhong.Wang@Sun.COM fcoe_worker_init(); 8929087SZhong.Wang@Sun.COM 8939087SZhong.Wang@Sun.COM ddi_report_dev(ss->ss_dip); 8949087SZhong.Wang@Sun.COM return (FCOE_SUCCESS); 8959087SZhong.Wang@Sun.COM } 8969087SZhong.Wang@Sun.COM 8979087SZhong.Wang@Sun.COM /* 8989087SZhong.Wang@Sun.COM * Finish final uninitialization 8999087SZhong.Wang@Sun.COM */ 9009087SZhong.Wang@Sun.COM static int 9019087SZhong.Wang@Sun.COM fcoe_detach_uninit(fcoe_soft_state_t *ss) 9029087SZhong.Wang@Sun.COM { 9039087SZhong.Wang@Sun.COM int ret; 9049087SZhong.Wang@Sun.COM if (!list_is_empty(&ss->ss_mac_list)) { 9059087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "ss_mac_list is not empty when detach"); 9069087SZhong.Wang@Sun.COM return (FCOE_FAILURE); 9079087SZhong.Wang@Sun.COM } 9089087SZhong.Wang@Sun.COM 9099087SZhong.Wang@Sun.COM if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) { 9109087SZhong.Wang@Sun.COM return (ret); 9119087SZhong.Wang@Sun.COM } 9129087SZhong.Wang@Sun.COM 9139087SZhong.Wang@Sun.COM /* 9149087SZhong.Wang@Sun.COM * Stop watchdog 9159087SZhong.Wang@Sun.COM */ 9169087SZhong.Wang@Sun.COM if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 9179087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_watch_mutex); 9189087SZhong.Wang@Sun.COM ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG; 9199087SZhong.Wang@Sun.COM cv_broadcast(&ss->ss_watch_cv); 9209087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_watch_mutex); 9219087SZhong.Wang@Sun.COM while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) { 9229087SZhong.Wang@Sun.COM delay(10); 9239087SZhong.Wang@Sun.COM } 9249087SZhong.Wang@Sun.COM } 9259087SZhong.Wang@Sun.COM 9269087SZhong.Wang@Sun.COM ddi_taskq_destroy(ss->ss_watchdog_taskq); 9279087SZhong.Wang@Sun.COM mutex_destroy(&ss->ss_watch_mutex); 9289087SZhong.Wang@Sun.COM cv_destroy(&ss->ss_watch_cv); 9299087SZhong.Wang@Sun.COM 9309087SZhong.Wang@Sun.COM ddi_remove_minor_node(ss->ss_dip, NULL); 9319087SZhong.Wang@Sun.COM mutex_destroy(&ss->ss_ioctl_mutex); 9329087SZhong.Wang@Sun.COM list_destroy(&ss->ss_mac_list); 9339087SZhong.Wang@Sun.COM 9349087SZhong.Wang@Sun.COM return (FCOE_SUCCESS); 9359087SZhong.Wang@Sun.COM } 9369087SZhong.Wang@Sun.COM 9379087SZhong.Wang@Sun.COM /* 9389087SZhong.Wang@Sun.COM * Return mac instance if it exist, or else return NULL. 9399087SZhong.Wang@Sun.COM */ 9409087SZhong.Wang@Sun.COM fcoe_mac_t * 9419307Skelly.hu@Sun.COM fcoe_lookup_mac_by_id(datalink_id_t linkid) 9429087SZhong.Wang@Sun.COM { 9439087SZhong.Wang@Sun.COM fcoe_mac_t *mac = NULL; 9449087SZhong.Wang@Sun.COM 9459307Skelly.hu@Sun.COM ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 9469087SZhong.Wang@Sun.COM for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 9479087SZhong.Wang@Sun.COM mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 9489307Skelly.hu@Sun.COM if (linkid != mac->fm_linkid) { 9499087SZhong.Wang@Sun.COM continue; 9509087SZhong.Wang@Sun.COM } 9519087SZhong.Wang@Sun.COM return (mac); 9529087SZhong.Wang@Sun.COM } 9539087SZhong.Wang@Sun.COM return (NULL); 9549087SZhong.Wang@Sun.COM } 9559087SZhong.Wang@Sun.COM 9569087SZhong.Wang@Sun.COM /* 9579087SZhong.Wang@Sun.COM * port wwn will start with 20:..., node wwn will start with 10:... 9589087SZhong.Wang@Sun.COM */ 9599087SZhong.Wang@Sun.COM static void 9609087SZhong.Wang@Sun.COM fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx) 9619087SZhong.Wang@Sun.COM { 9629087SZhong.Wang@Sun.COM ASSERT(wwn != NULL); 9639087SZhong.Wang@Sun.COM ASSERT(mac != NULL); 9649087SZhong.Wang@Sun.COM wwn[0] = (is_pwwn + 1) << 4; 9659087SZhong.Wang@Sun.COM wwn[1] = idx; 9669087SZhong.Wang@Sun.COM bcopy(mac, wwn + 2, ETHERADDRL); 9679087SZhong.Wang@Sun.COM } 9689087SZhong.Wang@Sun.COM 9699087SZhong.Wang@Sun.COM /* 9709087SZhong.Wang@Sun.COM * Return fcoe_mac if it exists, otherwise create a new one 9719087SZhong.Wang@Sun.COM */ 9729087SZhong.Wang@Sun.COM static fcoe_mac_t * 9739307Skelly.hu@Sun.COM fcoe_create_mac_by_id(datalink_id_t linkid) 9749087SZhong.Wang@Sun.COM { 9759087SZhong.Wang@Sun.COM fcoe_mac_t *mac = NULL; 9769307Skelly.hu@Sun.COM ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 9779087SZhong.Wang@Sun.COM 9789307Skelly.hu@Sun.COM mac = fcoe_lookup_mac_by_id(linkid); 9799087SZhong.Wang@Sun.COM if (mac != NULL) { 9809307Skelly.hu@Sun.COM FCOE_LOG("fcoe", "fcoe_create_mac_by_id found one mac %d", 9819307Skelly.hu@Sun.COM linkid); 9829087SZhong.Wang@Sun.COM return (mac); 9839087SZhong.Wang@Sun.COM } 9849087SZhong.Wang@Sun.COM 9859087SZhong.Wang@Sun.COM mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP); 9869307Skelly.hu@Sun.COM mac->fm_linkid = linkid; 9879087SZhong.Wang@Sun.COM mac->fm_flags = 0; 9889087SZhong.Wang@Sun.COM mac->fm_ss = fcoe_global_ss; 9899087SZhong.Wang@Sun.COM list_insert_tail(&mac->fm_ss->ss_mac_list, mac); 9909307Skelly.hu@Sun.COM FCOE_LOG("fcoe", "fcoe_create_mac_by_id created one mac %d", linkid); 9919087SZhong.Wang@Sun.COM return (mac); 9929087SZhong.Wang@Sun.COM } 9939087SZhong.Wang@Sun.COM 9949087SZhong.Wang@Sun.COM void 9959087SZhong.Wang@Sun.COM fcoe_destroy_mac(fcoe_mac_t *mac) 9969087SZhong.Wang@Sun.COM { 9979087SZhong.Wang@Sun.COM ASSERT(mac != NULL); 9989087SZhong.Wang@Sun.COM list_remove(&mac->fm_ss->ss_mac_list, mac); 9999087SZhong.Wang@Sun.COM kmem_free(mac, sizeof (fcoe_mac_t)); 10009087SZhong.Wang@Sun.COM } 10019087SZhong.Wang@Sun.COM 10029087SZhong.Wang@Sun.COM /* 10039087SZhong.Wang@Sun.COM * raw frame layout: 10049087SZhong.Wang@Sun.COM * ethernet header + vlan header (optional) + FCoE header + 10059087SZhong.Wang@Sun.COM * FC frame + FCoE tailer 10069087SZhong.Wang@Sun.COM */ 10079087SZhong.Wang@Sun.COM /* ARGSUSED */ 10089087SZhong.Wang@Sun.COM mblk_t * 10099087SZhong.Wang@Sun.COM fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size) 10109087SZhong.Wang@Sun.COM { 10119087SZhong.Wang@Sun.COM mblk_t *mp; 10129087SZhong.Wang@Sun.COM int err; 10139087SZhong.Wang@Sun.COM 10149087SZhong.Wang@Sun.COM /* 10159087SZhong.Wang@Sun.COM * FCFH_SIZE + PADDING_SIZE 10169087SZhong.Wang@Sun.COM */ 10179087SZhong.Wang@Sun.COM ASSERT(raw_frame_size >= 60); 10189087SZhong.Wang@Sun.COM while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) { 10199087SZhong.Wang@Sun.COM if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) { 10209087SZhong.Wang@Sun.COM FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err); 10219087SZhong.Wang@Sun.COM return (NULL); 10229087SZhong.Wang@Sun.COM } 10239087SZhong.Wang@Sun.COM } 10249087SZhong.Wang@Sun.COM mp->b_wptr = mp->b_rptr + raw_frame_size; 10259087SZhong.Wang@Sun.COM 10269087SZhong.Wang@Sun.COM /* 10279087SZhong.Wang@Sun.COM * We should always zero FC frame header 10289087SZhong.Wang@Sun.COM */ 10299087SZhong.Wang@Sun.COM bzero(mp->b_rptr + PADDING_HEADER_SIZE, 10309087SZhong.Wang@Sun.COM sizeof (fcoe_fc_frame_header_t)); 10319087SZhong.Wang@Sun.COM return (mp); 10329087SZhong.Wang@Sun.COM } 10339087SZhong.Wang@Sun.COM 10349087SZhong.Wang@Sun.COM static void 10359087SZhong.Wang@Sun.COM fcoe_watchdog(void *arg) 10369087SZhong.Wang@Sun.COM { 10379087SZhong.Wang@Sun.COM fcoe_soft_state_t *ss = (fcoe_soft_state_t *)arg; 10389087SZhong.Wang@Sun.COM fcoe_i_frame_t *fmi; 10399087SZhong.Wang@Sun.COM fcoe_mac_t *mac = NULL; 10409087SZhong.Wang@Sun.COM 10419087SZhong.Wang@Sun.COM FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss); 10429087SZhong.Wang@Sun.COM 10439087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_watch_mutex); 10449087SZhong.Wang@Sun.COM ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING; 10459087SZhong.Wang@Sun.COM while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) { 10469087SZhong.Wang@Sun.COM while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) { 10479087SZhong.Wang@Sun.COM list_remove(&ss->ss_pfrm_list, fmi); 10489087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_watch_mutex); 10499087SZhong.Wang@Sun.COM 10509087SZhong.Wang@Sun.COM mac = EPORT2MAC(fmi->fmi_frame->frm_eport); 10519087SZhong.Wang@Sun.COM mac->fm_client.ect_release_sol_frame(fmi->fmi_frame); 10529087SZhong.Wang@Sun.COM 10539087SZhong.Wang@Sun.COM mutex_enter(&ss->ss_watch_mutex); 10549087SZhong.Wang@Sun.COM mac->fm_frm_cnt--; 10559087SZhong.Wang@Sun.COM } 10569087SZhong.Wang@Sun.COM 10579087SZhong.Wang@Sun.COM ss->ss_flags |= SS_FLAG_DOG_WAITING; 10589087SZhong.Wang@Sun.COM (void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex); 10599087SZhong.Wang@Sun.COM ss->ss_flags &= ~SS_FLAG_DOG_WAITING; 10609087SZhong.Wang@Sun.COM } 10619087SZhong.Wang@Sun.COM 10629087SZhong.Wang@Sun.COM ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING; 10639087SZhong.Wang@Sun.COM mutex_exit(&ss->ss_watch_mutex); 10649087SZhong.Wang@Sun.COM } 10659087SZhong.Wang@Sun.COM 10669087SZhong.Wang@Sun.COM static void 10679087SZhong.Wang@Sun.COM fcoe_worker_init() 10689087SZhong.Wang@Sun.COM { 10699087SZhong.Wang@Sun.COM uint32_t i; 10709087SZhong.Wang@Sun.COM 10719087SZhong.Wang@Sun.COM fcoe_nworkers_running = 0; 10729087SZhong.Wang@Sun.COM fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ", 10739087SZhong.Wang@Sun.COM fcoe_nworkers, TASKQ_DEFAULTPRI, 0); 10749087SZhong.Wang@Sun.COM fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) * 10759087SZhong.Wang@Sun.COM fcoe_nworkers, KM_SLEEP); 10769087SZhong.Wang@Sun.COM for (i = 0; i < fcoe_nworkers; i++) { 10779087SZhong.Wang@Sun.COM fcoe_worker_t *w = &fcoe_workers[i]; 10789087SZhong.Wang@Sun.COM mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL); 10799087SZhong.Wang@Sun.COM cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL); 10809087SZhong.Wang@Sun.COM w->worker_flags &= ~FCOE_WORKER_TERMINATE; 10819087SZhong.Wang@Sun.COM list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t), 10829087SZhong.Wang@Sun.COM offsetof(fcoe_i_frame_t, fmi_pending_node)); 10839087SZhong.Wang@Sun.COM (void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame, 10849087SZhong.Wang@Sun.COM w, DDI_SLEEP); 10859087SZhong.Wang@Sun.COM } 10869087SZhong.Wang@Sun.COM while (fcoe_nworkers_running != fcoe_nworkers) { 10879087SZhong.Wang@Sun.COM delay(10); 10889087SZhong.Wang@Sun.COM } 10899087SZhong.Wang@Sun.COM } 10909087SZhong.Wang@Sun.COM 10919087SZhong.Wang@Sun.COM static int 10929087SZhong.Wang@Sun.COM fcoe_worker_fini() 10939087SZhong.Wang@Sun.COM { 10949087SZhong.Wang@Sun.COM uint32_t i; 10959087SZhong.Wang@Sun.COM 10969087SZhong.Wang@Sun.COM for (i = 0; i < fcoe_nworkers; i++) { 10979087SZhong.Wang@Sun.COM fcoe_worker_t *w = &fcoe_workers[i]; 10989087SZhong.Wang@Sun.COM mutex_enter(&w->worker_lock); 10999087SZhong.Wang@Sun.COM if (w->worker_flags & FCOE_WORKER_STARTED) { 11009087SZhong.Wang@Sun.COM w->worker_flags |= FCOE_WORKER_TERMINATE; 11019087SZhong.Wang@Sun.COM cv_signal(&w->worker_cv); 11029087SZhong.Wang@Sun.COM } 11039087SZhong.Wang@Sun.COM mutex_exit(&w->worker_lock); 11049087SZhong.Wang@Sun.COM } 11059087SZhong.Wang@Sun.COM 11069087SZhong.Wang@Sun.COM while (fcoe_nworkers_running != 0) { 11079087SZhong.Wang@Sun.COM delay(drv_usectohz(10000)); 11089087SZhong.Wang@Sun.COM } 11099087SZhong.Wang@Sun.COM 11109087SZhong.Wang@Sun.COM ddi_taskq_destroy(fcoe_worker_taskq); 11119087SZhong.Wang@Sun.COM kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers); 11129087SZhong.Wang@Sun.COM fcoe_workers = NULL; 11139087SZhong.Wang@Sun.COM return (FCOE_SUCCESS); 11149087SZhong.Wang@Sun.COM } 11159087SZhong.Wang@Sun.COM 11169087SZhong.Wang@Sun.COM static int 11179087SZhong.Wang@Sun.COM fcoe_crc_verify(fcoe_frame_t *frm) 11189087SZhong.Wang@Sun.COM { 11199087SZhong.Wang@Sun.COM uint32_t crc; 11209087SZhong.Wang@Sun.COM uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc; 11219087SZhong.Wang@Sun.COM uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) | 11229087SZhong.Wang@Sun.COM (crc_array[2] << 16) | (crc_array[3] << 24)); 11239087SZhong.Wang@Sun.COM CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table); 11249087SZhong.Wang@Sun.COM return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE); 11259087SZhong.Wang@Sun.COM } 11269087SZhong.Wang@Sun.COM 11279087SZhong.Wang@Sun.COM static void 11289087SZhong.Wang@Sun.COM fcoe_worker_frame(void *arg) 11299087SZhong.Wang@Sun.COM { 11309087SZhong.Wang@Sun.COM fcoe_worker_t *w = (fcoe_worker_t *)arg; 11319087SZhong.Wang@Sun.COM fcoe_i_frame_t *fmi; 11329087SZhong.Wang@Sun.COM int ret; 11339087SZhong.Wang@Sun.COM 11349087SZhong.Wang@Sun.COM atomic_add_32(&fcoe_nworkers_running, 1); 11359087SZhong.Wang@Sun.COM mutex_enter(&w->worker_lock); 11369087SZhong.Wang@Sun.COM w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE; 11379087SZhong.Wang@Sun.COM while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) { 11389087SZhong.Wang@Sun.COM /* 11399087SZhong.Wang@Sun.COM * loop through the frames 11409087SZhong.Wang@Sun.COM */ 11419087SZhong.Wang@Sun.COM while (fmi = list_head(&w->worker_frm_list)) { 11429087SZhong.Wang@Sun.COM list_remove(&w->worker_frm_list, fmi); 11439087SZhong.Wang@Sun.COM mutex_exit(&w->worker_lock); 11449087SZhong.Wang@Sun.COM /* 11459087SZhong.Wang@Sun.COM * do the checksum 11469087SZhong.Wang@Sun.COM */ 11479087SZhong.Wang@Sun.COM ret = fcoe_crc_verify(fmi->fmi_frame); 11489087SZhong.Wang@Sun.COM if (ret == FCOE_SUCCESS) { 11499087SZhong.Wang@Sun.COM fmi->fmi_mac->fm_client.ect_rx_frame( 11509087SZhong.Wang@Sun.COM fmi->fmi_frame); 11519087SZhong.Wang@Sun.COM } else { 11529087SZhong.Wang@Sun.COM fcoe_release_frame(fmi->fmi_frame); 11539087SZhong.Wang@Sun.COM } 11549087SZhong.Wang@Sun.COM mutex_enter(&w->worker_lock); 11559087SZhong.Wang@Sun.COM w->worker_ntasks--; 11569087SZhong.Wang@Sun.COM } 11579087SZhong.Wang@Sun.COM w->worker_flags &= ~FCOE_WORKER_ACTIVE; 11589087SZhong.Wang@Sun.COM cv_wait(&w->worker_cv, &w->worker_lock); 11599087SZhong.Wang@Sun.COM w->worker_flags |= FCOE_WORKER_ACTIVE; 11609087SZhong.Wang@Sun.COM } 11619087SZhong.Wang@Sun.COM w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE); 11629087SZhong.Wang@Sun.COM mutex_exit(&w->worker_lock); 11639087SZhong.Wang@Sun.COM atomic_add_32(&fcoe_nworkers_running, -1); 11649087SZhong.Wang@Sun.COM list_destroy(&w->worker_frm_list); 11659087SZhong.Wang@Sun.COM } 11669087SZhong.Wang@Sun.COM 11679087SZhong.Wang@Sun.COM void 11689087SZhong.Wang@Sun.COM fcoe_post_frame(fcoe_frame_t *frm) 11699087SZhong.Wang@Sun.COM { 11709087SZhong.Wang@Sun.COM fcoe_worker_t *w; 11719087SZhong.Wang@Sun.COM uint16_t oxid = FRM_OXID(frm); 11729087SZhong.Wang@Sun.COM 11739087SZhong.Wang@Sun.COM w = &fcoe_workers[oxid % fcoe_nworkers_running]; 11749087SZhong.Wang@Sun.COM mutex_enter(&w->worker_lock); 11759087SZhong.Wang@Sun.COM list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private); 11769087SZhong.Wang@Sun.COM w->worker_ntasks++; 11779087SZhong.Wang@Sun.COM if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) { 11789087SZhong.Wang@Sun.COM cv_signal(&w->worker_cv); 11799087SZhong.Wang@Sun.COM } 11809087SZhong.Wang@Sun.COM mutex_exit(&w->worker_lock); 11819087SZhong.Wang@Sun.COM } 11829087SZhong.Wang@Sun.COM 11839087SZhong.Wang@Sun.COM /* 11849087SZhong.Wang@Sun.COM * The max length of every LOG is 158 11859087SZhong.Wang@Sun.COM */ 11869087SZhong.Wang@Sun.COM void 11879087SZhong.Wang@Sun.COM fcoe_trace(caddr_t ident, const char *fmt, ...) 11889087SZhong.Wang@Sun.COM { 11899087SZhong.Wang@Sun.COM va_list args; 11909087SZhong.Wang@Sun.COM char tbuf[160]; 11919087SZhong.Wang@Sun.COM int len; 11929087SZhong.Wang@Sun.COM clock_t curclock; 11939087SZhong.Wang@Sun.COM clock_t usec; 11949087SZhong.Wang@Sun.COM 11959087SZhong.Wang@Sun.COM if (fcoe_trace_on == 0) { 11969087SZhong.Wang@Sun.COM return; 11979087SZhong.Wang@Sun.COM } 11989087SZhong.Wang@Sun.COM 11999087SZhong.Wang@Sun.COM curclock = ddi_get_lbolt(); 12009087SZhong.Wang@Sun.COM usec = (curclock - fcoe_trace_start) * usec_per_tick; 12019087SZhong.Wang@Sun.COM len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec / 12029087SZhong.Wang@Sun.COM (1000 * 1000)), ((usec % (1000 * 1000)) / 1000), 12039087SZhong.Wang@Sun.COM curclock, (ident ? ident : "unknown")); 12049087SZhong.Wang@Sun.COM va_start(args, fmt); 12059087SZhong.Wang@Sun.COM len += vsnprintf(tbuf + len, 158 - len, fmt, args); 12069087SZhong.Wang@Sun.COM va_end(args); 12079087SZhong.Wang@Sun.COM 12089087SZhong.Wang@Sun.COM if (len > 158) { 12099087SZhong.Wang@Sun.COM len = 158; 12109087SZhong.Wang@Sun.COM } 12119087SZhong.Wang@Sun.COM tbuf[len++] = '\n'; 12129087SZhong.Wang@Sun.COM tbuf[len] = 0; 12139087SZhong.Wang@Sun.COM 12149087SZhong.Wang@Sun.COM mutex_enter(&fcoe_trace_buf_lock); 12159087SZhong.Wang@Sun.COM bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1); 12169087SZhong.Wang@Sun.COM fcoe_trace_buf_curndx += len; 12179087SZhong.Wang@Sun.COM if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) { 12189087SZhong.Wang@Sun.COM fcoe_trace_buf_curndx = 0; 12199087SZhong.Wang@Sun.COM } 12209087SZhong.Wang@Sun.COM mutex_exit(&fcoe_trace_buf_lock); 12219087SZhong.Wang@Sun.COM } 12229087SZhong.Wang@Sun.COM 12239087SZhong.Wang@Sun.COM /* 12249087SZhong.Wang@Sun.COM * Check whether the pwwn or nwwn already exist or not 12259087SZhong.Wang@Sun.COM * Return value: 12269087SZhong.Wang@Sun.COM * 1: PWWN conflicted 12279087SZhong.Wang@Sun.COM * -1: NWWN conflicted 12289087SZhong.Wang@Sun.COM * 0: No conflict 12299087SZhong.Wang@Sun.COM */ 12309087SZhong.Wang@Sun.COM static int 12319087SZhong.Wang@Sun.COM fcoe_cmp_wwn(fcoe_mac_t *checkedmac) 12329087SZhong.Wang@Sun.COM { 12339087SZhong.Wang@Sun.COM fcoe_mac_t *mac; 12349087SZhong.Wang@Sun.COM uint8_t *nwwn, *pwwn, *cnwwn, *cpwwn; 12359087SZhong.Wang@Sun.COM 12369087SZhong.Wang@Sun.COM cnwwn = checkedmac->fm_eport.eport_nodewwn; 12379087SZhong.Wang@Sun.COM cpwwn = checkedmac->fm_eport.eport_portwwn; 12389307Skelly.hu@Sun.COM ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 12399087SZhong.Wang@Sun.COM 12409087SZhong.Wang@Sun.COM for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 12419087SZhong.Wang@Sun.COM mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 12429087SZhong.Wang@Sun.COM if (mac == checkedmac) { 12439087SZhong.Wang@Sun.COM continue; 12449087SZhong.Wang@Sun.COM } 12459087SZhong.Wang@Sun.COM nwwn = mac->fm_eport.eport_nodewwn; 12469087SZhong.Wang@Sun.COM pwwn = mac->fm_eport.eport_portwwn; 12479087SZhong.Wang@Sun.COM 12489087SZhong.Wang@Sun.COM if (memcmp(nwwn, cnwwn, 8) == 0) { 12499087SZhong.Wang@Sun.COM return (-1); 12509087SZhong.Wang@Sun.COM } 12519087SZhong.Wang@Sun.COM 12529087SZhong.Wang@Sun.COM if (memcmp(pwwn, cpwwn, 8) == 0) { 12539087SZhong.Wang@Sun.COM return (1); 12549087SZhong.Wang@Sun.COM } 12559087SZhong.Wang@Sun.COM } 12569087SZhong.Wang@Sun.COM return (0); 12579087SZhong.Wang@Sun.COM } 12589087SZhong.Wang@Sun.COM 12599087SZhong.Wang@Sun.COM static int 12609087SZhong.Wang@Sun.COM fcoe_get_port_list(fcoe_port_instance_t *ports, int count) 12619087SZhong.Wang@Sun.COM { 12629087SZhong.Wang@Sun.COM fcoe_mac_t *mac = NULL; 12639087SZhong.Wang@Sun.COM int i = 0; 12649087SZhong.Wang@Sun.COM 12659087SZhong.Wang@Sun.COM ASSERT(ports != NULL); 12669307Skelly.hu@Sun.COM ASSERT(MUTEX_HELD(&fcoe_global_ss->ss_ioctl_mutex)); 12679087SZhong.Wang@Sun.COM 12689087SZhong.Wang@Sun.COM for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac; 12699087SZhong.Wang@Sun.COM mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) { 12709087SZhong.Wang@Sun.COM if (i < count) { 12719087SZhong.Wang@Sun.COM bcopy(mac->fm_eport.eport_portwwn, 12729087SZhong.Wang@Sun.COM ports[i].fpi_pwwn, 8); 12739307Skelly.hu@Sun.COM ports[i].fpi_mac_linkid = mac->fm_linkid; 12749087SZhong.Wang@Sun.COM bcopy(mac->fm_current_addr, 12759087SZhong.Wang@Sun.COM ports[i].fpi_mac_current_addr, ETHERADDRL); 12769087SZhong.Wang@Sun.COM bcopy(mac->fm_primary_addr, 12779087SZhong.Wang@Sun.COM ports[i].fpi_mac_factory_addr, ETHERADDRL); 12789087SZhong.Wang@Sun.COM ports[i].fpi_port_type = 12799087SZhong.Wang@Sun.COM EPORT_CLT_TYPE(&mac->fm_eport); 12809087SZhong.Wang@Sun.COM ports[i].fpi_mtu_size = 12819087SZhong.Wang@Sun.COM mac->fm_eport.eport_mtu; 12829087SZhong.Wang@Sun.COM ports[i].fpi_mac_promisc = 12839087SZhong.Wang@Sun.COM mac->fm_promisc_handle != NULL ? 1 : 0; 12849087SZhong.Wang@Sun.COM } 12859087SZhong.Wang@Sun.COM i++; 12869087SZhong.Wang@Sun.COM } 12879087SZhong.Wang@Sun.COM return (i); 12889087SZhong.Wang@Sun.COM } 1289