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 mac->fm_eport.eport_link_speed = 203 mac_client_stat_get(mac->fm_cli_handle, MAC_STAT_IFSPEED); 204 205 /* 206 * Add a notify function so that we get updates from MAC 207 */ 208 mac->fm_notify_handle = mac_notify_add(mac->fm_handle, 209 fcoe_mac_notify, (void *)mac); 210 return (FCOE_SUCCESS); 211 } 212 213 int 214 fcoe_disable_callback(fcoe_mac_t *mac) 215 { 216 int ret; 217 218 if (mac->fm_promisc_handle) { 219 mac_promisc_remove(mac->fm_promisc_handle); 220 mac->fm_promisc_handle = NULL; 221 } else { 222 mac_rx_clear(mac->fm_cli_handle); 223 } 224 225 if (mac->fm_notify_handle) { 226 ret = mac_notify_remove(mac->fm_notify_handle, B_TRUE); 227 ASSERT(ret == 0); 228 mac->fm_notify_handle = NULL; 229 } 230 231 ret = fcoe_mac_set_address(&mac->fm_eport, 232 mac->fm_primary_addr, B_FALSE); 233 FCOE_SET_DEFAULT_FPORT_ADDR(mac->fm_eport.eport_efh_dst); 234 return (ret); 235 } 236 237 /* ARGSUSED */ 238 static void 239 fcoe_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t loopback) 240 { 241 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 242 mblk_t *next; 243 fcoe_frame_t *frm; 244 uint32_t raw_frame_size, frame_size; 245 uint16_t frm_type; 246 247 while (mp != NULL) { 248 next = mp->b_next; 249 mp->b_next = NULL; 250 frm_type = ntohs(*(uint16_t *)((uintptr_t)mp->b_rptr + 12)); 251 252 if (frm_type != ETHERTYPE_FCOE) { 253 /* 254 * This mp is not allocated in FCoE, but we must free it 255 */ 256 freeb(mp); 257 mp = next; 258 continue; 259 } 260 261 raw_frame_size = MBLKL(mp); 262 frame_size = raw_frame_size - PADDING_SIZE; 263 frm = fcoe_allocate_frame(&mac->fm_eport, frame_size, mp); 264 if (frm != NULL) { 265 frm->frm_clock = CURRENT_CLOCK; 266 fcoe_post_frame(frm); 267 } 268 269 mp = next; 270 } 271 } 272 273 static void 274 fcoe_mac_notify(void *arg, mac_notify_type_t type) 275 { 276 fcoe_mac_t *mac = (fcoe_mac_t *)arg; 277 278 /* 279 * We assume that the calls to this notification callback are serialized 280 * by MAC layer 281 */ 282 283 switch (type) { 284 case MAC_NOTE_LINK: 285 /* 286 * This notification is sent every time the MAC driver 287 * updates the link state. 288 */ 289 if (mac_stat_get(mac->fm_handle, MAC_STAT_LINK_UP) != 0) { 290 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_UP) { 291 break; 292 } 293 /* Get speed */ 294 mac->fm_eport.eport_link_speed = 295 mac_client_stat_get(mac->fm_cli_handle, 296 MAC_STAT_IFSPEED); 297 (void) fcoe_mac_set_address(&mac->fm_eport, 298 mac->fm_primary_addr, B_FALSE); 299 300 FCOE_SET_DEFAULT_FPORT_ADDR( 301 mac->fm_eport.eport_efh_dst); 302 303 mac->fm_link_state = FCOE_MAC_LINK_STATE_UP; 304 FCOE_LOG(NULL, 305 "fcoe_mac_notify: link/%d arg/%p LINK up", 306 mac->fm_linkid, arg, type); 307 fcoe_mac_notify_link_up(mac); 308 } else { 309 if (mac->fm_link_state == FCOE_MAC_LINK_STATE_DOWN) { 310 break; 311 } 312 mac->fm_link_state = FCOE_MAC_LINK_STATE_DOWN; 313 FCOE_LOG(NULL, 314 "fcoe_mac_notify: link/%d arg/%p LINK down", 315 mac->fm_linkid, arg, type); 316 fcoe_mac_notify_link_down(mac); 317 } 318 break; 319 320 case MAC_NOTE_TX: 321 /* 322 * MAC is not so busy now, then wake up fcoe_tx_frame to try 323 */ 324 mutex_enter(&mac->fm_mutex); 325 cv_broadcast(&mac->fm_tx_cv); 326 mutex_exit(&mac->fm_mutex); 327 328 FCOE_LOG("fcoe_mac_notify", "wake up"); 329 break; 330 331 default: 332 FCOE_LOG("fcoe_mac_notify", "not supported arg/%p, type/%d", 333 arg, type); 334 break; 335 } 336 } 337 338 int 339 fcoe_mac_set_address(fcoe_port_t *eport, uint8_t *addr, boolean_t fc_assigned) 340 { 341 fcoe_mac_t *mac = EPORT2MAC(eport); 342 int ret; 343 344 if (bcmp(addr, mac->fm_current_addr, 6) == 0) { 345 return (FCOE_SUCCESS); 346 } 347 348 mutex_enter(&mac->fm_mutex); 349 if (mac->fm_promisc_handle == NULL) { 350 ret = mac_unicast_primary_set(mac->fm_handle, addr); 351 if (ret != 0) { 352 mutex_exit(&mac->fm_mutex); 353 FCOE_LOG("fcoe", "mac_unicast_primary_set on %d " 354 "failed %x", mac->fm_linkid, ret); 355 return (FCOE_FAILURE); 356 } 357 } 358 if (fc_assigned) { 359 bcopy(addr, mac->fm_current_addr, ETHERADDRL); 360 } else { 361 bcopy(mac->fm_primary_addr, 362 mac->fm_current_addr, ETHERADDRL); 363 } 364 mutex_exit(&mac->fm_mutex); 365 return (FCOE_SUCCESS); 366 } 367