1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/stat.h> 27 #include <sys/types.h> 28 #include <netinet/in.h> 29 #include <sys/inttypes.h> 30 #include <sys/strsun.h> 31 #include <sys/mac_client.h> 32 33 /* 34 * FCoE header files 35 */ 36 #include <sys/fcoe/fcoeio.h> 37 #include <sys/fcoe/fcoe_common.h> 38 39 /* 40 * Driver's own header files 41 */ 42 #include <fcoe.h> 43 #include <fcoe_eth.h> 44 #include <fcoe_fc.h> 45 46 static void fcoe_rx(void *arg, mac_resource_handle_t mrh, 47 mblk_t *mp, boolean_t loopback); 48 static void fcoe_mac_notify(void *arg, mac_notify_type_t type); 49 50 /* 51 * Global variable definitions 52 */ 53 54 /* 55 * Internal tunable, used to enable p2p mode 56 */ 57 volatile uint32_t fcoe_enable_p2pmode = 0; 58 59 int 60 fcoe_open_mac(fcoe_mac_t *mac, int force_promisc, fcoeio_stat_t *err_detail) 61 { 62 int ret; 63 int fcoe_ret; 64 char cli_name[MAXNAMELEN]; 65 mac_diag_t diag; 66 uint16_t fm_open_flag = 0; 67 68 *err_detail = 0; 69 70 /* 71 * Open MAC interface 72 */ 73 ret = mac_open_by_linkname(mac->fm_link_name, &mac->fm_handle); 74 if (ret != 0) { 75 cmn_err(CE_WARN, "Open network interface %s failed", 76 mac->fm_link_name); 77 FCOE_LOG("fcoe", "mac_open_by_linkname %s failed %x", 78 mac->fm_link_name, ret); 79 return (FCOE_FAILURE); 80 } 81 82 if (mac_is_vnic(mac->fm_handle)) { 83 (void) mac_close(mac->fm_handle); 84 *err_detail = FCOEIOE_VNIC_UNSUPPORT; 85 return (FCOE_FAILURE); 86 } 87 88 (void) sprintf(cli_name, "%s-%s", mac->fm_link_name, "fcoe"); 89 90 ret = mac_client_open(mac->fm_handle, 91 &mac->fm_cli_handle, cli_name, fm_open_flag); 92 if (ret != 0) { 93 (void) fcoe_close_mac(mac); 94 return (FCOE_FAILURE); 95 } 96 /* 97 * Cache the pointer of the immutable MAC inforamtion and 98 * the current and primary MAC address 99 */ 100 mac_unicast_primary_get(mac->fm_handle, mac->fm_primary_addr); 101 bcopy(mac->fm_primary_addr, mac->fm_current_addr, 102 ETHERADDRL); 103 104 if (mac_unicast_add(mac->fm_cli_handle, NULL, MAC_UNICAST_PRIMARY, 105 &mac->fm_unicst_handle, 0, &diag)) { 106 (void) fcoe_close_mac(mac); 107 return (FCOE_FAILURE); 108 } 109 110 if (force_promisc) { 111 mac->fm_force_promisc = B_TRUE; 112 } 113 114 /* Get mtu */ 115 mac_sdu_get(mac->fm_handle, NULL, &mac->fm_eport.eport_mtu); 116 if (mac->fm_eport.eport_mtu < FCOE_MIN_MTU_SIZE) { 117 if (!fcoe_enable_p2pmode || mac->fm_eport.eport_mtu < 1500) { 118 /* 119 * Fail open if fail to get mtu, or we are not 120 * using p2p, or we are using p2p, but 121 * the mtu is too small 122 */ 123 (void) fcoe_close_mac(mac); 124 *err_detail = FCOEIOE_NEED_JUMBO_FRAME; 125 return (FCOE_FAILURE); 126 } 127 } 128 129 mac->fm_eport.eport_link_speed = 130 mac_client_stat_get(mac->fm_cli_handle, MAC_STAT_IFSPEED); 131 132 cv_init(&mac->fm_tx_cv, NULL, CV_DRIVER, NULL); 133 mutex_init(&mac->fm_mutex, NULL, MUTEX_DRIVER, NULL); 134 mac->fm_running = B_TRUE; 135 136 fcoe_ret = FCOE_SUCCESS; 137 return (fcoe_ret); 138 } 139 140 int 141 fcoe_close_mac(fcoe_mac_t *mac) 142 { 143 int ret; 144 145 if (mac->fm_handle == NULL) { 146 return (FCOE_SUCCESS); 147 } 148 149 if (mac->fm_running) { 150 cv_destroy(&mac->fm_tx_cv); 151 mutex_destroy(&mac->fm_mutex); 152 mac->fm_running = B_FALSE; 153 } 154 155 if (mac->fm_promisc_handle != NULL) { 156 mac_promisc_remove(mac->fm_promisc_handle); 157 mac->fm_promisc_handle = NULL; 158 } else { 159 mac_rx_clear(mac->fm_cli_handle); 160 } 161 162 if (mac->fm_notify_handle != NULL) { 163 ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE); 164 ASSERT(ret == 0); 165 mac->fm_notify_handle = NULL; 166 } 167 168 if (mac->fm_unicst_handle != NULL) { 169 (void) mac_unicast_remove(mac->fm_cli_handle, 170 mac->fm_unicst_handle); 171 mac->fm_unicst_handle = NULL; 172 } 173 174 mac_client_close(mac->fm_cli_handle, 0); 175 mac->fm_cli_handle = NULL; 176 177 (void) mac_close(mac->fm_handle); 178 mac->fm_handle = NULL; 179 180 return (FCOE_SUCCESS); 181 } 182 183 int 184 fcoe_enable_callback(fcoe_mac_t *mac) 185 { 186 int ret; 187 188 /* 189 * Set message callback 190 */ 191 if (mac->fm_force_promisc) { 192 ret = mac_promisc_add(mac->fm_cli_handle, 193 MAC_CLIENT_PROMISC_FILTERED, fcoe_rx, mac, 194 &mac->fm_promisc_handle, 195 MAC_PROMISC_FLAGS_NO_TX_LOOP); 196 if (ret != 0) { 197 cmn_err(CE_WARN, "Enable promisc mode on %s failed", 198 mac->fm_link_name); 199 FCOE_LOG("foce", "mac_promisc_add on %s failed %x", 200 mac->fm_link_name, ret); 201 return (FCOE_FAILURE); 202 } 203 } else { 204 mac_rx_set(mac->fm_cli_handle, fcoe_rx, mac); 205 } 206 207 /* Get the link state, if it's up, we will need to notify client */ 208 mac->fm_link_state = 209 mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP)? 210 FCOE_MAC_LINK_STATE_UP:FCOE_MAC_LINK_STATE_DOWN; 211 212 /* 213 * Add a notify function so that we get updates from MAC 214 */ 215 mac->fm_notify_handle = mac_notify_add(mac->fm_handle, 216 fcoe_mac_notify, (void *)mac); 217 return (FCOE_SUCCESS); 218 } 219 220 int 221 fcoe_disable_callback(fcoe_mac_t *mac) 222 { 223 int ret; 224 225 if (mac->fm_promisc_handle) { 226 mac_promisc_remove(mac->fm_promisc_handle); 227 mac->fm_promisc_handle = NULL; 228 } else { 229 mac_rx_clear(mac->fm_cli_handle); 230 } 231 232 if (mac->fm_notify_handle) { 233 ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE); 234 ASSERT(ret == 0); 235 mac->fm_notify_handle = NULL; 236 } 237 238 ret = fcoe_mac_set_address(&mac->fm_eport, 239 mac->fm_primary_addr, B_FALSE); 240 FCOE_SET_DEFAULT_FPORT_ADDR(mac->fm_eport.eport_efh_dst); 241 return (ret); 242 } 243 244 /* ARGSUSED */ 245 static void 246 fcoe_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t loopback) 247 { 248 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 249 mblk_t *next; 250 fcoe_frame_t *frm; 251 uint32_t raw_frame_size, frame_size; 252 uint16_t frm_type; 253 254 while (mp != NULL) { 255 next = mp->b_next; 256 mp->b_next = NULL; 257 frm_type = ntohs(*(uint16_t *)((uintptr_t)mp->b_rptr + 12)); 258 259 if (frm_type != ETHERTYPE_FCOE) { 260 /* 261 * This mp is not allocated in FCoE, but we must free it 262 */ 263 freeb(mp); 264 mp = next; 265 continue; 266 } 267 268 raw_frame_size = MBLKL(mp); 269 frame_size = raw_frame_size - PADDING_SIZE; 270 frm = fcoe_allocate_frame(&mac->fm_eport, frame_size, mp); 271 if (frm != NULL) { 272 fcoe_post_frame(frm); 273 } 274 275 mp = next; 276 } 277 } 278 279 static void 280 fcoe_mac_notify(void *arg, mac_notify_type_t type) 281 { 282 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 283 284 /* 285 * We assume that the calls to this notification callback are serialized 286 * by MAC layer 287 */ 288 289 switch (type) { 290 case MAC_NOTE_LINK: 291 /* 292 * This notification is sent every time the MAC driver 293 * updates the link state. 294 */ 295 if (mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP) != 0) { 296 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_UP) { 297 break; 298 } 299 /* Get speed */ 300 mac->fm_eport.eport_link_speed = 301 mac_client_stat_get(mac->fm_cli_handle, 302 MAC_STAT_IFSPEED); 303 304 (void) fcoe_mac_set_address(&mac->fm_eport, 305 mac->fm_primary_addr, B_FALSE); 306 307 FCOE_SET_DEFAULT_FPORT_ADDR( 308 mac->fm_eport.eport_efh_dst); 309 310 mac->fm_link_state = FCOE_MAC_LINK_STATE_UP; 311 FCOE_LOG(mac->fm_link_name, 312 "fcoe_mac_notify: arg/%p LINK up", arg, type); 313 fcoe_mac_notify_link_up(mac); 314 } else { 315 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_DOWN) { 316 break; 317 } 318 mac->fm_link_state = FCOE_MAC_LINK_STATE_DOWN; 319 FCOE_LOG(mac->fm_link_name, 320 "fcoe_mac_notify: arg/%p LINK down", arg, type); 321 fcoe_mac_notify_link_down(mac); 322 } 323 break; 324 325 case MAC_NOTE_TX: 326 /* 327 * MAC is not so busy now, then wake up fcoe_tx_frame to try 328 */ 329 mutex_enter(&mac->fm_mutex); 330 cv_broadcast(&mac->fm_tx_cv); 331 mutex_exit(&mac->fm_mutex); 332 333 FCOE_LOG("fcoe_mac_notify", "wake up"); 334 break; 335 336 default: 337 FCOE_LOG("fcoe_mac_notify", "not supported arg/%p, type/%d", 338 arg, type); 339 break; 340 } 341 } 342 343 int 344 fcoe_mac_set_address(fcoe_port_t *eport, uint8_t *addr, boolean_t fc_assigned) 345 { 346 fcoe_mac_t *mac = EPORT2MAC(eport); 347 int ret; 348 349 if (bcmp(addr, mac->fm_current_addr, 6) == 0) { 350 return (FCOE_SUCCESS); 351 } 352 353 mutex_enter(&mac->fm_mutex); 354 if (mac->fm_promisc_handle == NULL) { 355 ret = mac_unicast_primary_set(mac->fm_handle, addr); 356 if (ret != 0) { 357 mutex_exit(&mac->fm_mutex); 358 cmn_err(CE_WARN, "Set primary unicast address on %s " 359 "failed", mac->fm_link_name); 360 FCOE_LOG("fcoe", "mac_unicast_primary_set on %s " 361 "failed %x", mac->fm_link_name, ret); 362 return (FCOE_FAILURE); 363 } 364 } 365 if (fc_assigned) { 366 bcopy(addr, mac->fm_current_addr, ETHERADDRL); 367 } else { 368 bcopy(mac->fm_primary_addr, 369 mac->fm_current_addr, ETHERADDRL); 370 } 371 mutex_exit(&mac->fm_mutex); 372 return (FCOE_SUCCESS); 373 } 374