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_linkid(mac->fm_linkid, &mac->fm_handle); 74 if (ret != 0) { 75 FCOE_LOG("fcoe", "mac_open_by_linkname %d failed %x", 76 mac->fm_linkid, ret); 77 return (FCOE_FAILURE); 78 } 79 80 (void) sprintf(cli_name, "%s-%d", "fcoe", mac->fm_linkid); 81 82 ret = mac_client_open(mac->fm_handle, 83 &mac->fm_cli_handle, cli_name, fm_open_flag); 84 if (ret != 0) { 85 (void) fcoe_close_mac(mac); 86 return (FCOE_FAILURE); 87 } 88 /* 89 * Cache the pointer of the immutable MAC inforamtion and 90 * the current and primary MAC address 91 */ 92 mac_unicast_primary_get(mac->fm_handle, mac->fm_primary_addr); 93 bcopy(mac->fm_primary_addr, mac->fm_current_addr, 94 ETHERADDRL); 95 96 if (mac_unicast_add(mac->fm_cli_handle, NULL, MAC_UNICAST_PRIMARY, 97 &mac->fm_unicst_handle, 0, &diag)) { 98 (void) fcoe_close_mac(mac); 99 return (FCOE_FAILURE); 100 } 101 102 if (force_promisc) { 103 mac->fm_force_promisc = B_TRUE; 104 } 105 106 /* Get mtu */ 107 mac_sdu_get(mac->fm_handle, NULL, &mac->fm_eport.eport_mtu); 108 if (mac->fm_eport.eport_mtu < FCOE_MIN_MTU_SIZE) { 109 if (!fcoe_enable_p2pmode || mac->fm_eport.eport_mtu < 1500) { 110 /* 111 * Fail open if fail to get mtu, or we are not 112 * using p2p, or we are using p2p, but 113 * the mtu is too small 114 */ 115 (void) fcoe_close_mac(mac); 116 *err_detail = FCOEIOE_NEED_JUMBO_FRAME; 117 return (FCOE_FAILURE); 118 } 119 } 120 121 mac->fm_eport.eport_link_speed = 122 mac_client_stat_get(mac->fm_cli_handle, MAC_STAT_IFSPEED); 123 124 cv_init(&mac->fm_tx_cv, NULL, CV_DRIVER, NULL); 125 mutex_init(&mac->fm_mutex, NULL, MUTEX_DRIVER, NULL); 126 mac->fm_running = B_TRUE; 127 128 fcoe_ret = FCOE_SUCCESS; 129 return (fcoe_ret); 130 } 131 132 int 133 fcoe_close_mac(fcoe_mac_t *mac) 134 { 135 int ret; 136 137 if (mac->fm_handle == NULL) { 138 return (FCOE_SUCCESS); 139 } 140 141 if (mac->fm_running) { 142 cv_destroy(&mac->fm_tx_cv); 143 mutex_destroy(&mac->fm_mutex); 144 mac->fm_running = B_FALSE; 145 } 146 147 if (mac->fm_promisc_handle != NULL) { 148 mac_promisc_remove(mac->fm_promisc_handle); 149 mac->fm_promisc_handle = NULL; 150 } else { 151 mac_rx_clear(mac->fm_cli_handle); 152 } 153 154 if (mac->fm_notify_handle != NULL) { 155 ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE); 156 ASSERT(ret == 0); 157 mac->fm_notify_handle = NULL; 158 } 159 160 if (mac->fm_unicst_handle != NULL) { 161 (void) mac_unicast_remove(mac->fm_cli_handle, 162 mac->fm_unicst_handle); 163 mac->fm_unicst_handle = NULL; 164 } 165 166 mac_client_close(mac->fm_cli_handle, 0); 167 mac->fm_cli_handle = NULL; 168 169 (void) mac_close(mac->fm_handle); 170 mac->fm_handle = NULL; 171 172 return (FCOE_SUCCESS); 173 } 174 175 int 176 fcoe_enable_callback(fcoe_mac_t *mac) 177 { 178 int ret; 179 180 /* 181 * Set message callback 182 */ 183 if (mac->fm_force_promisc) { 184 ret = mac_promisc_add(mac->fm_cli_handle, 185 MAC_CLIENT_PROMISC_FILTERED, fcoe_rx, mac, 186 &mac->fm_promisc_handle, 187 MAC_PROMISC_FLAGS_NO_TX_LOOP); 188 if (ret != 0) { 189 FCOE_LOG("foce", "mac_promisc_add on %d failed %x", 190 mac->fm_linkid, ret); 191 return (FCOE_FAILURE); 192 } 193 } else { 194 mac_rx_set(mac->fm_cli_handle, fcoe_rx, mac); 195 } 196 197 /* Get the link state, if it's up, we will need to notify client */ 198 mac->fm_link_state = 199 mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP)? 200 FCOE_MAC_LINK_STATE_UP:FCOE_MAC_LINK_STATE_DOWN; 201 202 /* 203 * Add a notify function so that we get updates from MAC 204 */ 205 mac->fm_notify_handle = mac_notify_add(mac->fm_handle, 206 fcoe_mac_notify, (void *)mac); 207 return (FCOE_SUCCESS); 208 } 209 210 int 211 fcoe_disable_callback(fcoe_mac_t *mac) 212 { 213 int ret; 214 215 if (mac->fm_promisc_handle) { 216 mac_promisc_remove(mac->fm_promisc_handle); 217 mac->fm_promisc_handle = NULL; 218 } else { 219 mac_rx_clear(mac->fm_cli_handle); 220 } 221 222 if (mac->fm_notify_handle) { 223 ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE); 224 ASSERT(ret == 0); 225 mac->fm_notify_handle = NULL; 226 } 227 228 ret = fcoe_mac_set_address(&mac->fm_eport, 229 mac->fm_primary_addr, B_FALSE); 230 FCOE_SET_DEFAULT_FPORT_ADDR(mac->fm_eport.eport_efh_dst); 231 return (ret); 232 } 233 234 /* ARGSUSED */ 235 static void 236 fcoe_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t loopback) 237 { 238 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 239 mblk_t *next; 240 fcoe_frame_t *frm; 241 uint32_t raw_frame_size, frame_size; 242 uint16_t frm_type; 243 244 while (mp != NULL) { 245 next = mp->b_next; 246 mp->b_next = NULL; 247 frm_type = ntohs(*(uint16_t *)((uintptr_t)mp->b_rptr + 12)); 248 249 if (frm_type != ETHERTYPE_FCOE) { 250 /* 251 * This mp is not allocated in FCoE, but we must free it 252 */ 253 freeb(mp); 254 mp = next; 255 continue; 256 } 257 258 raw_frame_size = MBLKL(mp); 259 frame_size = raw_frame_size - PADDING_SIZE; 260 frm = fcoe_allocate_frame(&mac->fm_eport, frame_size, mp); 261 if (frm != NULL) { 262 fcoe_post_frame(frm); 263 } 264 265 mp = next; 266 } 267 } 268 269 static void 270 fcoe_mac_notify(void *arg, mac_notify_type_t type) 271 { 272 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 273 274 /* 275 * We assume that the calls to this notification callback are serialized 276 * by MAC layer 277 */ 278 279 switch (type) { 280 case MAC_NOTE_LINK: 281 /* 282 * This notification is sent every time the MAC driver 283 * updates the link state. 284 */ 285 if (mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP) != 0) { 286 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_UP) { 287 break; 288 } 289 /* Get speed */ 290 mac->fm_eport.eport_link_speed = 291 mac_client_stat_get(mac->fm_cli_handle, 292 MAC_STAT_IFSPEED); 293 294 (void) fcoe_mac_set_address(&mac->fm_eport, 295 mac->fm_primary_addr, B_FALSE); 296 297 FCOE_SET_DEFAULT_FPORT_ADDR( 298 mac->fm_eport.eport_efh_dst); 299 300 mac->fm_link_state = FCOE_MAC_LINK_STATE_UP; 301 FCOE_LOG(NULL, 302 "fcoe_mac_notify: link/%d arg/%p LINK up", 303 mac->fm_linkid, arg, type); 304 fcoe_mac_notify_link_up(mac); 305 } else { 306 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_DOWN) { 307 break; 308 } 309 mac->fm_link_state = FCOE_MAC_LINK_STATE_DOWN; 310 FCOE_LOG(NULL, 311 "fcoe_mac_notify: link/%d arg/%p LINK down", 312 mac->fm_linkid, arg, type); 313 fcoe_mac_notify_link_down(mac); 314 } 315 break; 316 317 case MAC_NOTE_TX: 318 /* 319 * MAC is not so busy now, then wake up fcoe_tx_frame to try 320 */ 321 mutex_enter(&mac->fm_mutex); 322 cv_broadcast(&mac->fm_tx_cv); 323 mutex_exit(&mac->fm_mutex); 324 325 FCOE_LOG("fcoe_mac_notify", "wake up"); 326 break; 327 328 default: 329 FCOE_LOG("fcoe_mac_notify", "not supported arg/%p, type/%d", 330 arg, type); 331 break; 332 } 333 } 334 335 int 336 fcoe_mac_set_address(fcoe_port_t *eport, uint8_t *addr, boolean_t fc_assigned) 337 { 338 fcoe_mac_t *mac = EPORT2MAC(eport); 339 int ret; 340 341 if (bcmp(addr, mac->fm_current_addr, 6) == 0) { 342 return (FCOE_SUCCESS); 343 } 344 345 mutex_enter(&mac->fm_mutex); 346 if (mac->fm_promisc_handle == NULL) { 347 ret = mac_unicast_primary_set(mac->fm_handle, addr); 348 if (ret != 0) { 349 mutex_exit(&mac->fm_mutex); 350 FCOE_LOG("fcoe", "mac_unicast_primary_set on %d " 351 "failed %x", mac->fm_linkid, ret); 352 return (FCOE_FAILURE); 353 } 354 } 355 if (fc_assigned) { 356 bcopy(addr, mac->fm_current_addr, ETHERADDRL); 357 } else { 358 bcopy(mac->fm_primary_addr, 359 mac->fm_current_addr, ETHERADDRL); 360 } 361 mutex_exit(&mac->fm_mutex); 362 return (FCOE_SUCCESS); 363 } 364