18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2008 Cisco Systems, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * Copyright 2007 Nuova Systems, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify 68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 78c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 108c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 118c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 128c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 138c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 158c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 168c2ecf20Sopenharmony_ci * SOFTWARE. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#include <linux/errno.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 248c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 258c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 268c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 278c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fip.h> 288c2ecf20Sopenharmony_ci#include <scsi/fc/fc_els.h> 298c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fcoe.h> 308c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h> 318c2ecf20Sopenharmony_ci#include <scsi/libfc.h> 328c2ecf20Sopenharmony_ci#include "fnic_io.h" 338c2ecf20Sopenharmony_ci#include "fnic.h" 348c2ecf20Sopenharmony_ci#include "fnic_fip.h" 358c2ecf20Sopenharmony_ci#include "cq_enet_desc.h" 368c2ecf20Sopenharmony_ci#include "cq_exch_desc.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS; 398c2ecf20Sopenharmony_cistruct workqueue_struct *fnic_fip_queue; 408c2ecf20Sopenharmony_cistruct workqueue_struct *fnic_event_queue; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *); 438c2ecf20Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic); 448c2ecf20Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic); 458c2ecf20Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *); 468c2ecf20Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag); 478c2ecf20Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_civoid fnic_handle_link(struct work_struct *work) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct fnic *fnic = container_of(work, struct fnic, link_work); 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci int old_link_status; 548c2ecf20Sopenharmony_ci u32 old_link_down_cnt; 558c2ecf20Sopenharmony_ci u64 old_port_speed, new_port_speed; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 618c2ecf20Sopenharmony_ci return; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci old_link_down_cnt = fnic->link_down_cnt; 658c2ecf20Sopenharmony_ci old_link_status = fnic->link_status; 668c2ecf20Sopenharmony_ci old_port_speed = atomic64_read( 678c2ecf20Sopenharmony_ci &fnic->fnic_stats.misc_stats.current_port_speed); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci fnic->link_status = vnic_dev_link_status(fnic->vdev); 708c2ecf20Sopenharmony_ci fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci new_port_speed = vnic_dev_port_speed(fnic->vdev); 738c2ecf20Sopenharmony_ci atomic64_set(&fnic->fnic_stats.misc_stats.current_port_speed, 748c2ecf20Sopenharmony_ci new_port_speed); 758c2ecf20Sopenharmony_ci if (old_port_speed != new_port_speed) 768c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 778c2ecf20Sopenharmony_ci "Current vnic speed set to : %llu\n", 788c2ecf20Sopenharmony_ci new_port_speed); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci switch (vnic_dev_port_speed(fnic->vdev)) { 818c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_10G: 828c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_10GBIT; 838c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_10GBIT; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_20G: 868c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_20GBIT; 878c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_20GBIT; 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_25G: 908c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_25GBIT; 918c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_25GBIT; 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_40G: 948c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_4x10G: 958c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_40GBIT; 968c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_40GBIT; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci case DCEM_PORTSPEED_100G: 998c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_100GBIT; 1008c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_100GBIT; 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci default: 1038c2ecf20Sopenharmony_ci fc_host_speed(fnic->lport->host) = FC_PORTSPEED_UNKNOWN; 1048c2ecf20Sopenharmony_ci fnic->lport->link_supported_speeds = FC_PORTSPEED_UNKNOWN; 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (old_link_status == fnic->link_status) { 1098c2ecf20Sopenharmony_ci if (!fnic->link_status) { 1108c2ecf20Sopenharmony_ci /* DOWN -> DOWN */ 1118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 1128c2ecf20Sopenharmony_ci fnic_fc_trace_set_data(fnic->lport->host->host_no, 1138c2ecf20Sopenharmony_ci FNIC_FC_LE, "Link Status: DOWN->DOWN", 1148c2ecf20Sopenharmony_ci strlen("Link Status: DOWN->DOWN")); 1158c2ecf20Sopenharmony_ci } else { 1168c2ecf20Sopenharmony_ci if (old_link_down_cnt != fnic->link_down_cnt) { 1178c2ecf20Sopenharmony_ci /* UP -> DOWN -> UP */ 1188c2ecf20Sopenharmony_ci fnic->lport->host_stats.link_failure_count++; 1198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 1208c2ecf20Sopenharmony_ci fnic_fc_trace_set_data( 1218c2ecf20Sopenharmony_ci fnic->lport->host->host_no, 1228c2ecf20Sopenharmony_ci FNIC_FC_LE, 1238c2ecf20Sopenharmony_ci "Link Status:UP_DOWN_UP", 1248c2ecf20Sopenharmony_ci strlen("Link_Status:UP_DOWN_UP") 1258c2ecf20Sopenharmony_ci ); 1268c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 1278c2ecf20Sopenharmony_ci "link down\n"); 1288c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&fnic->ctlr); 1298c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FIP_CAPABLE) { 1308c2ecf20Sopenharmony_ci /* start FCoE VLAN discovery */ 1318c2ecf20Sopenharmony_ci fnic_fc_trace_set_data( 1328c2ecf20Sopenharmony_ci fnic->lport->host->host_no, 1338c2ecf20Sopenharmony_ci FNIC_FC_LE, 1348c2ecf20Sopenharmony_ci "Link Status: UP_DOWN_UP_VLAN", 1358c2ecf20Sopenharmony_ci strlen( 1368c2ecf20Sopenharmony_ci "Link Status: UP_DOWN_UP_VLAN") 1378c2ecf20Sopenharmony_ci ); 1388c2ecf20Sopenharmony_ci fnic_fcoe_send_vlan_req(fnic); 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 1428c2ecf20Sopenharmony_ci "link up\n"); 1438c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&fnic->ctlr); 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci /* UP -> UP */ 1468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 1478c2ecf20Sopenharmony_ci fnic_fc_trace_set_data( 1488c2ecf20Sopenharmony_ci fnic->lport->host->host_no, FNIC_FC_LE, 1498c2ecf20Sopenharmony_ci "Link Status: UP_UP", 1508c2ecf20Sopenharmony_ci strlen("Link Status: UP_UP")); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } else if (fnic->link_status) { 1548c2ecf20Sopenharmony_ci /* DOWN -> UP */ 1558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 1568c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FIP_CAPABLE) { 1578c2ecf20Sopenharmony_ci /* start FCoE VLAN discovery */ 1588c2ecf20Sopenharmony_ci fnic_fc_trace_set_data( 1598c2ecf20Sopenharmony_ci fnic->lport->host->host_no, 1608c2ecf20Sopenharmony_ci FNIC_FC_LE, "Link Status: DOWN_UP_VLAN", 1618c2ecf20Sopenharmony_ci strlen("Link Status: DOWN_UP_VLAN")); 1628c2ecf20Sopenharmony_ci fnic_fcoe_send_vlan_req(fnic); 1638c2ecf20Sopenharmony_ci return; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n"); 1668c2ecf20Sopenharmony_ci fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_LE, 1678c2ecf20Sopenharmony_ci "Link Status: DOWN_UP", strlen("Link Status: DOWN_UP")); 1688c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&fnic->ctlr); 1698c2ecf20Sopenharmony_ci } else { 1708c2ecf20Sopenharmony_ci /* UP -> DOWN */ 1718c2ecf20Sopenharmony_ci fnic->lport->host_stats.link_failure_count++; 1728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 1738c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n"); 1748c2ecf20Sopenharmony_ci fnic_fc_trace_set_data( 1758c2ecf20Sopenharmony_ci fnic->lport->host->host_no, FNIC_FC_LE, 1768c2ecf20Sopenharmony_ci "Link Status: UP_DOWN", 1778c2ecf20Sopenharmony_ci strlen("Link Status: UP_DOWN")); 1788c2ecf20Sopenharmony_ci if (fnic->config.flags & VFCF_FIP_CAPABLE) { 1798c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 1808c2ecf20Sopenharmony_ci "deleting fip-timer during link-down\n"); 1818c2ecf20Sopenharmony_ci del_timer_sync(&fnic->fip_timer); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&fnic->ctlr); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* 1898c2ecf20Sopenharmony_ci * This function passes incoming fabric frames to libFC 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_civoid fnic_handle_frame(struct work_struct *work) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct fnic *fnic = container_of(work, struct fnic, frame_work); 1948c2ecf20Sopenharmony_ci struct fc_lport *lp = fnic->lport; 1958c2ecf20Sopenharmony_ci unsigned long flags; 1968c2ecf20Sopenharmony_ci struct sk_buff *skb; 1978c2ecf20Sopenharmony_ci struct fc_frame *fp; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&fnic->frame_queue))) { 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 2028c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 2038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2048c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2058c2ecf20Sopenharmony_ci return; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * If we're in a transitional state, just re-queue and return. 2118c2ecf20Sopenharmony_ci * The queue will be serviced when we get to a stable state. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci if (fnic->state != FNIC_IN_FC_MODE && 2148c2ecf20Sopenharmony_ci fnic->state != FNIC_IN_ETH_MODE) { 2158c2ecf20Sopenharmony_ci skb_queue_head(&fnic->frame_queue, skb); 2168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci fc_exch_recv(lp, fp); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_civoid fnic_fcoe_evlist_free(struct fnic *fnic) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct fnic_event *fevt = NULL; 2288c2ecf20Sopenharmony_ci struct fnic_event *next = NULL; 2298c2ecf20Sopenharmony_ci unsigned long flags; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 2328c2ecf20Sopenharmony_ci if (list_empty(&fnic->evlist)) { 2338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci list_for_each_entry_safe(fevt, next, &fnic->evlist, list) { 2388c2ecf20Sopenharmony_ci list_del(&fevt->list); 2398c2ecf20Sopenharmony_ci kfree(fevt); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_civoid fnic_handle_event(struct work_struct *work) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct fnic *fnic = container_of(work, struct fnic, event_work); 2478c2ecf20Sopenharmony_ci struct fnic_event *fevt = NULL; 2488c2ecf20Sopenharmony_ci struct fnic_event *next = NULL; 2498c2ecf20Sopenharmony_ci unsigned long flags; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 2528c2ecf20Sopenharmony_ci if (list_empty(&fnic->evlist)) { 2538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2548c2ecf20Sopenharmony_ci return; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci list_for_each_entry_safe(fevt, next, &fnic->evlist, list) { 2588c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 2598c2ecf20Sopenharmony_ci list_del(&fevt->list); 2608c2ecf20Sopenharmony_ci kfree(fevt); 2618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2628c2ecf20Sopenharmony_ci return; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * If we're in a transitional state, just re-queue and return. 2668c2ecf20Sopenharmony_ci * The queue will be serviced when we get to a stable state. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci if (fnic->state != FNIC_IN_FC_MODE && 2698c2ecf20Sopenharmony_ci fnic->state != FNIC_IN_ETH_MODE) { 2708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2718c2ecf20Sopenharmony_ci return; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci list_del(&fevt->list); 2758c2ecf20Sopenharmony_ci switch (fevt->event) { 2768c2ecf20Sopenharmony_ci case FNIC_EVT_START_VLAN_DISC: 2778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2788c2ecf20Sopenharmony_ci fnic_fcoe_send_vlan_req(fnic); 2798c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case FNIC_EVT_START_FCF_DISC: 2828c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 2838c2ecf20Sopenharmony_ci "Start FCF Discovery\n"); 2848c2ecf20Sopenharmony_ci fnic_fcoe_start_fcf_disc(fnic); 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 2888c2ecf20Sopenharmony_ci "Unknown event 0x%x\n", fevt->event); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci kfree(fevt); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/** 2978c2ecf20Sopenharmony_ci * Check if the Received FIP FLOGI frame is rejected 2988c2ecf20Sopenharmony_ci * @fip: The FCoE controller that received the frame 2998c2ecf20Sopenharmony_ci * @skb: The received FIP frame 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * Returns non-zero if the frame is rejected with unsupported cmd with 3028c2ecf20Sopenharmony_ci * insufficient resource els explanation. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic inline int is_fnic_fip_flogi_reject(struct fcoe_ctlr *fip, 3058c2ecf20Sopenharmony_ci struct sk_buff *skb) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct fc_lport *lport = fip->lp; 3088c2ecf20Sopenharmony_ci struct fip_header *fiph; 3098c2ecf20Sopenharmony_ci struct fc_frame_header *fh = NULL; 3108c2ecf20Sopenharmony_ci struct fip_desc *desc; 3118c2ecf20Sopenharmony_ci struct fip_encaps *els; 3128c2ecf20Sopenharmony_ci u16 op; 3138c2ecf20Sopenharmony_ci u8 els_op; 3148c2ecf20Sopenharmony_ci u8 sub; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci size_t rlen; 3178c2ecf20Sopenharmony_ci size_t dlen = 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (skb_linearize(skb)) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (skb->len < sizeof(*fiph)) 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci fiph = (struct fip_header *)skb->data; 3268c2ecf20Sopenharmony_ci op = ntohs(fiph->fip_op); 3278c2ecf20Sopenharmony_ci sub = fiph->fip_subcode; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (op != FIP_OP_LS) 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (sub != FIP_SC_REP) 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci rlen = ntohs(fiph->fip_dl_len) * 4; 3368c2ecf20Sopenharmony_ci if (rlen + sizeof(*fiph) > skb->len) 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci desc = (struct fip_desc *)(fiph + 1); 3408c2ecf20Sopenharmony_ci dlen = desc->fip_dlen * FIP_BPW; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (desc->fip_dtype == FIP_DT_FLOGI) { 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (dlen < sizeof(*els) + sizeof(*fh) + 1) 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci els = (struct fip_encaps *)desc; 3488c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *)(els + 1); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!fh) 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * ELS command code, reason and explanation should be = Reject, 3558c2ecf20Sopenharmony_ci * unsupported command and insufficient resource 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci els_op = *(u8 *)(fh + 1); 3588c2ecf20Sopenharmony_ci if (els_op == ELS_LS_RJT) { 3598c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, lport->host, 3608c2ecf20Sopenharmony_ci "Flogi Request Rejected by Switch\n"); 3618c2ecf20Sopenharmony_ci return 1; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, lport->host, 3648c2ecf20Sopenharmony_ci "Flogi Request Accepted by Switch\n"); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void fnic_fcoe_send_vlan_req(struct fnic *fnic) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = &fnic->ctlr; 3728c2ecf20Sopenharmony_ci struct fnic_stats *fnic_stats = &fnic->fnic_stats; 3738c2ecf20Sopenharmony_ci struct sk_buff *skb; 3748c2ecf20Sopenharmony_ci char *eth_fr; 3758c2ecf20Sopenharmony_ci struct fip_vlan *vlan; 3768c2ecf20Sopenharmony_ci u64 vlan_tov; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci fnic_fcoe_reset_vlans(fnic); 3798c2ecf20Sopenharmony_ci fnic->set_vlan(fnic, 0); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (printk_ratelimit()) 3828c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, 3838c2ecf20Sopenharmony_ci "Sending VLAN request...\n"); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci skb = dev_alloc_skb(sizeof(struct fip_vlan)); 3868c2ecf20Sopenharmony_ci if (!skb) 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci eth_fr = (char *)skb->data; 3908c2ecf20Sopenharmony_ci vlan = (struct fip_vlan *)eth_fr; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci memset(vlan, 0, sizeof(*vlan)); 3938c2ecf20Sopenharmony_ci memcpy(vlan->eth.h_source, fip->ctl_src_addr, ETH_ALEN); 3948c2ecf20Sopenharmony_ci memcpy(vlan->eth.h_dest, fcoe_all_fcfs, ETH_ALEN); 3958c2ecf20Sopenharmony_ci vlan->eth.h_proto = htons(ETH_P_FIP); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci vlan->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER); 3988c2ecf20Sopenharmony_ci vlan->fip.fip_op = htons(FIP_OP_VLAN); 3998c2ecf20Sopenharmony_ci vlan->fip.fip_subcode = FIP_SC_VL_REQ; 4008c2ecf20Sopenharmony_ci vlan->fip.fip_dl_len = htons(sizeof(vlan->desc) / FIP_BPW); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci vlan->desc.mac.fd_desc.fip_dtype = FIP_DT_MAC; 4038c2ecf20Sopenharmony_ci vlan->desc.mac.fd_desc.fip_dlen = sizeof(vlan->desc.mac) / FIP_BPW; 4048c2ecf20Sopenharmony_ci memcpy(&vlan->desc.mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci vlan->desc.wwnn.fd_desc.fip_dtype = FIP_DT_NAME; 4078c2ecf20Sopenharmony_ci vlan->desc.wwnn.fd_desc.fip_dlen = sizeof(vlan->desc.wwnn) / FIP_BPW; 4088c2ecf20Sopenharmony_ci put_unaligned_be64(fip->lp->wwnn, &vlan->desc.wwnn.fd_wwn); 4098c2ecf20Sopenharmony_ci atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci skb_put(skb, sizeof(*vlan)); 4128c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_FIP); 4138c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 4148c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 4158c2ecf20Sopenharmony_ci fip->send(fip, skb); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* set a timer so that we can retry if there no response */ 4188c2ecf20Sopenharmony_ci vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV); 4198c2ecf20Sopenharmony_ci mod_timer(&fnic->fip_timer, round_jiffies(vlan_tov)); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct sk_buff *skb) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = &fnic->ctlr; 4258c2ecf20Sopenharmony_ci struct fip_header *fiph; 4268c2ecf20Sopenharmony_ci struct fip_desc *desc; 4278c2ecf20Sopenharmony_ci struct fnic_stats *fnic_stats = &fnic->fnic_stats; 4288c2ecf20Sopenharmony_ci u16 vid; 4298c2ecf20Sopenharmony_ci size_t rlen; 4308c2ecf20Sopenharmony_ci size_t dlen; 4318c2ecf20Sopenharmony_ci struct fcoe_vlan *vlan; 4328c2ecf20Sopenharmony_ci u64 sol_time; 4338c2ecf20Sopenharmony_ci unsigned long flags; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, 4368c2ecf20Sopenharmony_ci "Received VLAN response...\n"); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci fiph = (struct fip_header *) skb->data; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, 4418c2ecf20Sopenharmony_ci "Received VLAN response... OP 0x%x SUB_OP 0x%x\n", 4428c2ecf20Sopenharmony_ci ntohs(fiph->fip_op), fiph->fip_subcode); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci rlen = ntohs(fiph->fip_dl_len) * 4; 4458c2ecf20Sopenharmony_ci fnic_fcoe_reset_vlans(fnic); 4468c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->vlans_lock, flags); 4478c2ecf20Sopenharmony_ci desc = (struct fip_desc *)(fiph + 1); 4488c2ecf20Sopenharmony_ci while (rlen > 0) { 4498c2ecf20Sopenharmony_ci dlen = desc->fip_dlen * FIP_BPW; 4508c2ecf20Sopenharmony_ci switch (desc->fip_dtype) { 4518c2ecf20Sopenharmony_ci case FIP_DT_VLAN: 4528c2ecf20Sopenharmony_ci vid = ntohs(((struct fip_vlan_desc *)desc)->fd_vlan); 4538c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 4548c2ecf20Sopenharmony_ci "process_vlan_resp: FIP VLAN %d\n", vid); 4558c2ecf20Sopenharmony_ci vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC); 4568c2ecf20Sopenharmony_ci if (!vlan) { 4578c2ecf20Sopenharmony_ci /* retry from timer */ 4588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, 4598c2ecf20Sopenharmony_ci flags); 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci vlan->vid = vid & 0x0fff; 4638c2ecf20Sopenharmony_ci vlan->state = FIP_VLAN_AVAIL; 4648c2ecf20Sopenharmony_ci list_add_tail(&vlan->list, &fnic->vlans); 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci desc = (struct fip_desc *)((char *)desc + dlen); 4688c2ecf20Sopenharmony_ci rlen -= dlen; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* any VLAN descriptors present ? */ 4728c2ecf20Sopenharmony_ci if (list_empty(&fnic->vlans)) { 4738c2ecf20Sopenharmony_ci /* retry from timer */ 4748c2ecf20Sopenharmony_ci atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID); 4758c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_INFO, fnic->lport->host, 4768c2ecf20Sopenharmony_ci "No VLAN descriptors in FIP VLAN response\n"); 4778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list); 4828c2ecf20Sopenharmony_ci fnic->set_vlan(fnic, vlan->vid); 4838c2ecf20Sopenharmony_ci vlan->state = FIP_VLAN_SENT; /* sent now */ 4848c2ecf20Sopenharmony_ci vlan->sol_count++; 4858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* start the solicitation */ 4888c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(fip); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY); 4918c2ecf20Sopenharmony_ci mod_timer(&fnic->fip_timer, round_jiffies(sol_time)); 4928c2ecf20Sopenharmony_ciout: 4938c2ecf20Sopenharmony_ci return; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void fnic_fcoe_start_fcf_disc(struct fnic *fnic) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci unsigned long flags; 4998c2ecf20Sopenharmony_ci struct fcoe_vlan *vlan; 5008c2ecf20Sopenharmony_ci u64 sol_time; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->vlans_lock, flags); 5038c2ecf20Sopenharmony_ci vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list); 5048c2ecf20Sopenharmony_ci fnic->set_vlan(fnic, vlan->vid); 5058c2ecf20Sopenharmony_ci vlan->state = FIP_VLAN_SENT; /* sent now */ 5068c2ecf20Sopenharmony_ci vlan->sol_count = 1; 5078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* start the solicitation */ 5108c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(&fnic->ctlr); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci sol_time = jiffies + msecs_to_jiffies(FCOE_CTLR_START_DELAY); 5138c2ecf20Sopenharmony_ci mod_timer(&fnic->fip_timer, round_jiffies(sol_time)); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int fnic_fcoe_vlan_check(struct fnic *fnic, u16 flag) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci unsigned long flags; 5198c2ecf20Sopenharmony_ci struct fcoe_vlan *fvlan; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->vlans_lock, flags); 5228c2ecf20Sopenharmony_ci if (list_empty(&fnic->vlans)) { 5238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 5248c2ecf20Sopenharmony_ci return -EINVAL; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci fvlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list); 5288c2ecf20Sopenharmony_ci if (fvlan->state == FIP_VLAN_USED) { 5298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (fvlan->state == FIP_VLAN_SENT) { 5348c2ecf20Sopenharmony_ci fvlan->state = FIP_VLAN_USED; 5358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void fnic_event_enq(struct fnic *fnic, enum fnic_evt ev) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci struct fnic_event *fevt; 5458c2ecf20Sopenharmony_ci unsigned long flags; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci fevt = kmalloc(sizeof(*fevt), GFP_ATOMIC); 5488c2ecf20Sopenharmony_ci if (!fevt) 5498c2ecf20Sopenharmony_ci return; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci fevt->fnic = fnic; 5528c2ecf20Sopenharmony_ci fevt->event = ev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 5558c2ecf20Sopenharmony_ci list_add_tail(&fevt->list, &fnic->evlist); 5568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci schedule_work(&fnic->event_work); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic int fnic_fcoe_handle_fip_frame(struct fnic *fnic, struct sk_buff *skb) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct fip_header *fiph; 5648c2ecf20Sopenharmony_ci int ret = 1; 5658c2ecf20Sopenharmony_ci u16 op; 5668c2ecf20Sopenharmony_ci u8 sub; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!skb || !(skb->data)) 5698c2ecf20Sopenharmony_ci return -1; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (skb_linearize(skb)) 5728c2ecf20Sopenharmony_ci goto drop; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci fiph = (struct fip_header *)skb->data; 5758c2ecf20Sopenharmony_ci op = ntohs(fiph->fip_op); 5768c2ecf20Sopenharmony_ci sub = fiph->fip_subcode; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER) 5798c2ecf20Sopenharmony_ci goto drop; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len) 5828c2ecf20Sopenharmony_ci goto drop; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (op == FIP_OP_DISC && sub == FIP_SC_ADV) { 5858c2ecf20Sopenharmony_ci if (fnic_fcoe_vlan_check(fnic, ntohs(fiph->fip_flags))) 5868c2ecf20Sopenharmony_ci goto drop; 5878c2ecf20Sopenharmony_ci /* pass it on to fcoe */ 5888c2ecf20Sopenharmony_ci ret = 1; 5898c2ecf20Sopenharmony_ci } else if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) { 5908c2ecf20Sopenharmony_ci /* set the vlan as used */ 5918c2ecf20Sopenharmony_ci fnic_fcoe_process_vlan_resp(fnic, skb); 5928c2ecf20Sopenharmony_ci ret = 0; 5938c2ecf20Sopenharmony_ci } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) { 5948c2ecf20Sopenharmony_ci /* received CVL request, restart vlan disc */ 5958c2ecf20Sopenharmony_ci fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC); 5968c2ecf20Sopenharmony_ci /* pass it on to fcoe */ 5978c2ecf20Sopenharmony_ci ret = 1; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_cidrop: 6008c2ecf20Sopenharmony_ci return ret; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_civoid fnic_handle_fip_frame(struct work_struct *work) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct fnic *fnic = container_of(work, struct fnic, fip_frame_work); 6068c2ecf20Sopenharmony_ci struct fnic_stats *fnic_stats = &fnic->fnic_stats; 6078c2ecf20Sopenharmony_ci unsigned long flags; 6088c2ecf20Sopenharmony_ci struct sk_buff *skb; 6098c2ecf20Sopenharmony_ci struct ethhdr *eh; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&fnic->fip_frame_queue))) { 6128c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 6138c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 6148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 6158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6168c2ecf20Sopenharmony_ci return; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci /* 6198c2ecf20Sopenharmony_ci * If we're in a transitional state, just re-queue and return. 6208c2ecf20Sopenharmony_ci * The queue will be serviced when we get to a stable state. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci if (fnic->state != FNIC_IN_FC_MODE && 6238c2ecf20Sopenharmony_ci fnic->state != FNIC_IN_ETH_MODE) { 6248c2ecf20Sopenharmony_ci skb_queue_head(&fnic->fip_frame_queue, skb); 6258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 6268c2ecf20Sopenharmony_ci return; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 6298c2ecf20Sopenharmony_ci eh = (struct ethhdr *)skb->data; 6308c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_FIP)) { 6318c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*eh)); 6328c2ecf20Sopenharmony_ci if (fnic_fcoe_handle_fip_frame(fnic, skb) <= 0) { 6338c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6348c2ecf20Sopenharmony_ci continue; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * If there's FLOGI rejects - clear all 6388c2ecf20Sopenharmony_ci * fcf's & restart from scratch 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci if (is_fnic_fip_flogi_reject(&fnic->ctlr, skb)) { 6418c2ecf20Sopenharmony_ci atomic64_inc( 6428c2ecf20Sopenharmony_ci &fnic_stats->vlan_stats.flogi_rejects); 6438c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 6448c2ecf20Sopenharmony_ci "Trigger a Link down - VLAN Disc\n"); 6458c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(&fnic->ctlr); 6468c2ecf20Sopenharmony_ci /* start FCoE VLAN discovery */ 6478c2ecf20Sopenharmony_ci fnic_fcoe_send_vlan_req(fnic); 6488c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6498c2ecf20Sopenharmony_ci continue; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci fcoe_ctlr_recv(&fnic->ctlr, skb); 6528c2ecf20Sopenharmony_ci continue; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/** 6588c2ecf20Sopenharmony_ci * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame. 6598c2ecf20Sopenharmony_ci * @fnic: fnic instance. 6608c2ecf20Sopenharmony_ci * @skb: Ethernet Frame. 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_cistatic inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct fc_frame *fp; 6658c2ecf20Sopenharmony_ci struct ethhdr *eh; 6668c2ecf20Sopenharmony_ci struct fcoe_hdr *fcoe_hdr; 6678c2ecf20Sopenharmony_ci struct fcoe_crc_eof *ft; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * Undo VLAN encapsulation if present. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci eh = (struct ethhdr *)skb->data; 6738c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_8021Q)) { 6748c2ecf20Sopenharmony_ci memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); 6758c2ecf20Sopenharmony_ci eh = skb_pull(skb, VLAN_HLEN); 6768c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_FIP)) { 6798c2ecf20Sopenharmony_ci if (!(fnic->config.flags & VFCF_FIP_CAPABLE)) { 6808c2ecf20Sopenharmony_ci printk(KERN_ERR "Dropped FIP frame, as firmware " 6818c2ecf20Sopenharmony_ci "uses non-FIP mode, Enable FIP " 6828c2ecf20Sopenharmony_ci "using UCSM\n"); 6838c2ecf20Sopenharmony_ci goto drop; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, 6868c2ecf20Sopenharmony_ci FNIC_FC_RECV|0x80, (char *)skb->data, skb->len)) != 0) { 6878c2ecf20Sopenharmony_ci printk(KERN_ERR "fnic ctlr frame trace error!!!"); 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci skb_queue_tail(&fnic->fip_frame_queue, skb); 6908c2ecf20Sopenharmony_ci queue_work(fnic_fip_queue, &fnic->fip_frame_work); 6918c2ecf20Sopenharmony_ci return 1; /* let caller know packet was used */ 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci if (eh->h_proto != htons(ETH_P_FCOE)) 6948c2ecf20Sopenharmony_ci goto drop; 6958c2ecf20Sopenharmony_ci skb_set_network_header(skb, sizeof(*eh)); 6968c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*eh)); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci fcoe_hdr = (struct fcoe_hdr *)skb->data; 6998c2ecf20Sopenharmony_ci if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER) 7008c2ecf20Sopenharmony_ci goto drop; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 7038c2ecf20Sopenharmony_ci fc_frame_init(fp); 7048c2ecf20Sopenharmony_ci fr_sof(fp) = fcoe_hdr->fcoe_sof; 7058c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct fcoe_hdr)); 7068c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft)); 7098c2ecf20Sopenharmony_ci fr_eof(fp) = ft->fcoe_eof; 7108c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - sizeof(*ft)); 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_cidrop: 7138c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 7148c2ecf20Sopenharmony_ci return -1; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * fnic_update_mac_locked() - set data MAC address and filters. 7198c2ecf20Sopenharmony_ci * @fnic: fnic instance. 7208c2ecf20Sopenharmony_ci * @new: newly-assigned FCoE MAC address. 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * Called with the fnic lock held. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_civoid fnic_update_mac_locked(struct fnic *fnic, u8 *new) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci u8 *ctl = fnic->ctlr.ctl_src_addr; 7278c2ecf20Sopenharmony_ci u8 *data = fnic->data_src_addr; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (is_zero_ether_addr(new)) 7308c2ecf20Sopenharmony_ci new = ctl; 7318c2ecf20Sopenharmony_ci if (ether_addr_equal(data, new)) 7328c2ecf20Sopenharmony_ci return; 7338c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new); 7348c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl)) 7358c2ecf20Sopenharmony_ci vnic_dev_del_addr(fnic->vdev, data); 7368c2ecf20Sopenharmony_ci memcpy(data, new, ETH_ALEN); 7378c2ecf20Sopenharmony_ci if (!ether_addr_equal(new, ctl)) 7388c2ecf20Sopenharmony_ci vnic_dev_add_addr(fnic->vdev, new); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/** 7428c2ecf20Sopenharmony_ci * fnic_update_mac() - set data MAC address and filters. 7438c2ecf20Sopenharmony_ci * @lport: local port. 7448c2ecf20Sopenharmony_ci * @new: newly-assigned FCoE MAC address. 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_civoid fnic_update_mac(struct fc_lport *lport, u8 *new) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lport); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci spin_lock_irq(&fnic->fnic_lock); 7518c2ecf20Sopenharmony_ci fnic_update_mac_locked(fnic, new); 7528c2ecf20Sopenharmony_ci spin_unlock_irq(&fnic->fnic_lock); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/** 7568c2ecf20Sopenharmony_ci * fnic_set_port_id() - set the port_ID after successful FLOGI. 7578c2ecf20Sopenharmony_ci * @lport: local port. 7588c2ecf20Sopenharmony_ci * @port_id: assigned FC_ID. 7598c2ecf20Sopenharmony_ci * @fp: received frame containing the FLOGI accept or NULL. 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * This is called from libfc when a new FC_ID has been assigned. 7628c2ecf20Sopenharmony_ci * This causes us to reset the firmware to FC_MODE and setup the new MAC 7638c2ecf20Sopenharmony_ci * address and FC_ID. 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * It is also called with FC_ID 0 when we're logged off. 7668c2ecf20Sopenharmony_ci * 7678c2ecf20Sopenharmony_ci * If the FC_ID is due to point-to-point, fp may be NULL. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_civoid fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lport); 7728c2ecf20Sopenharmony_ci u8 *mac; 7738c2ecf20Sopenharmony_ci int ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n", 7768c2ecf20Sopenharmony_ci port_id, fp); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * If we're clearing the FC_ID, change to use the ctl_src_addr. 7808c2ecf20Sopenharmony_ci * Set ethernet mode to send FLOGI. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci if (!port_id) { 7838c2ecf20Sopenharmony_ci fnic_update_mac(lport, fnic->ctlr.ctl_src_addr); 7848c2ecf20Sopenharmony_ci fnic_set_eth_mode(fnic); 7858c2ecf20Sopenharmony_ci return; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (fp) { 7898c2ecf20Sopenharmony_ci mac = fr_cb(fp)->granted_mac; 7908c2ecf20Sopenharmony_ci if (is_zero_ether_addr(mac)) { 7918c2ecf20Sopenharmony_ci /* non-FIP - FLOGI already accepted - ignore return */ 7928c2ecf20Sopenharmony_ci fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci fnic_update_mac(lport, mac); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* Change state to reflect transition to FC mode */ 7988c2ecf20Sopenharmony_ci spin_lock_irq(&fnic->fnic_lock); 7998c2ecf20Sopenharmony_ci if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE) 8008c2ecf20Sopenharmony_ci fnic->state = FNIC_IN_ETH_TRANS_FC_MODE; 8018c2ecf20Sopenharmony_ci else { 8028c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 8038c2ecf20Sopenharmony_ci "Unexpected fnic state %s while" 8048c2ecf20Sopenharmony_ci " processing flogi resp\n", 8058c2ecf20Sopenharmony_ci fnic_state_to_str(fnic->state)); 8068c2ecf20Sopenharmony_ci spin_unlock_irq(&fnic->fnic_lock); 8078c2ecf20Sopenharmony_ci return; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci spin_unlock_irq(&fnic->fnic_lock); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* 8128c2ecf20Sopenharmony_ci * Send FLOGI registration to firmware to set up FC mode. 8138c2ecf20Sopenharmony_ci * The new address will be set up when registration completes. 8148c2ecf20Sopenharmony_ci */ 8158c2ecf20Sopenharmony_ci ret = fnic_flogi_reg_handler(fnic, port_id); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (ret < 0) { 8188c2ecf20Sopenharmony_ci spin_lock_irq(&fnic->fnic_lock); 8198c2ecf20Sopenharmony_ci if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) 8208c2ecf20Sopenharmony_ci fnic->state = FNIC_IN_ETH_MODE; 8218c2ecf20Sopenharmony_ci spin_unlock_irq(&fnic->fnic_lock); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc 8268c2ecf20Sopenharmony_ci *cq_desc, struct vnic_rq_buf *buf, 8278c2ecf20Sopenharmony_ci int skipped __attribute__((unused)), 8288c2ecf20Sopenharmony_ci void *opaque) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(rq->vdev); 8318c2ecf20Sopenharmony_ci struct sk_buff *skb; 8328c2ecf20Sopenharmony_ci struct fc_frame *fp; 8338c2ecf20Sopenharmony_ci struct fnic_stats *fnic_stats = &fnic->fnic_stats; 8348c2ecf20Sopenharmony_ci u8 type, color, eop, sop, ingress_port, vlan_stripped; 8358c2ecf20Sopenharmony_ci u8 fcoe = 0, fcoe_sof, fcoe_eof; 8368c2ecf20Sopenharmony_ci u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0; 8378c2ecf20Sopenharmony_ci u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; 8388c2ecf20Sopenharmony_ci u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc; 8398c2ecf20Sopenharmony_ci u8 fcs_ok = 1, packet_error = 0; 8408c2ecf20Sopenharmony_ci u16 q_number, completed_index, bytes_written = 0, vlan, checksum; 8418c2ecf20Sopenharmony_ci u32 rss_hash; 8428c2ecf20Sopenharmony_ci u16 exchange_id, tmpl; 8438c2ecf20Sopenharmony_ci u8 sof = 0; 8448c2ecf20Sopenharmony_ci u8 eof = 0; 8458c2ecf20Sopenharmony_ci u32 fcp_bytes_written = 0; 8468c2ecf20Sopenharmony_ci unsigned long flags; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, 8498c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 8508c2ecf20Sopenharmony_ci skb = buf->os_buf; 8518c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 8528c2ecf20Sopenharmony_ci buf->os_buf = NULL; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index); 8558c2ecf20Sopenharmony_ci if (type == CQ_DESC_TYPE_RQ_FCP) { 8568c2ecf20Sopenharmony_ci cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc, 8578c2ecf20Sopenharmony_ci &type, &color, &q_number, &completed_index, 8588c2ecf20Sopenharmony_ci &eop, &sop, &fcoe_fc_crc_ok, &exchange_id, 8598c2ecf20Sopenharmony_ci &tmpl, &fcp_bytes_written, &sof, &eof, 8608c2ecf20Sopenharmony_ci &ingress_port, &packet_error, 8618c2ecf20Sopenharmony_ci &fcoe_enc_error, &fcs_ok, &vlan_stripped, 8628c2ecf20Sopenharmony_ci &vlan); 8638c2ecf20Sopenharmony_ci skb_trim(skb, fcp_bytes_written); 8648c2ecf20Sopenharmony_ci fr_sof(fp) = sof; 8658c2ecf20Sopenharmony_ci fr_eof(fp) = eof; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci } else if (type == CQ_DESC_TYPE_RQ_ENET) { 8688c2ecf20Sopenharmony_ci cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc, 8698c2ecf20Sopenharmony_ci &type, &color, &q_number, &completed_index, 8708c2ecf20Sopenharmony_ci &ingress_port, &fcoe, &eop, &sop, 8718c2ecf20Sopenharmony_ci &rss_type, &csum_not_calc, &rss_hash, 8728c2ecf20Sopenharmony_ci &bytes_written, &packet_error, 8738c2ecf20Sopenharmony_ci &vlan_stripped, &vlan, &checksum, 8748c2ecf20Sopenharmony_ci &fcoe_sof, &fcoe_fc_crc_ok, 8758c2ecf20Sopenharmony_ci &fcoe_enc_error, &fcoe_eof, 8768c2ecf20Sopenharmony_ci &tcp_udp_csum_ok, &udp, &tcp, 8778c2ecf20Sopenharmony_ci &ipv4_csum_ok, &ipv6, &ipv4, 8788c2ecf20Sopenharmony_ci &ipv4_fragment, &fcs_ok); 8798c2ecf20Sopenharmony_ci skb_trim(skb, bytes_written); 8808c2ecf20Sopenharmony_ci if (!fcs_ok) { 8818c2ecf20Sopenharmony_ci atomic64_inc(&fnic_stats->misc_stats.frame_errors); 8828c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 8838c2ecf20Sopenharmony_ci "fcs error. dropping packet.\n"); 8848c2ecf20Sopenharmony_ci goto drop; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci if (fnic_import_rq_eth_pkt(fnic, skb)) 8878c2ecf20Sopenharmony_ci return; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci } else { 8908c2ecf20Sopenharmony_ci /* wrong CQ type*/ 8918c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 8928c2ecf20Sopenharmony_ci "fnic rq_cmpl wrong cq type x%x\n", type); 8938c2ecf20Sopenharmony_ci goto drop; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) { 8978c2ecf20Sopenharmony_ci atomic64_inc(&fnic_stats->misc_stats.frame_errors); 8988c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 8998c2ecf20Sopenharmony_ci "fnic rq_cmpl fcoe x%x fcsok x%x" 9008c2ecf20Sopenharmony_ci " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err" 9018c2ecf20Sopenharmony_ci " x%x\n", 9028c2ecf20Sopenharmony_ci fcoe, fcs_ok, packet_error, 9038c2ecf20Sopenharmony_ci fcoe_fc_crc_ok, fcoe_enc_error); 9048c2ecf20Sopenharmony_ci goto drop; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 9088c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 9098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 9108c2ecf20Sopenharmony_ci goto drop; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci fr_dev(fp) = fnic->lport; 9138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 9148c2ecf20Sopenharmony_ci if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_RECV, 9158c2ecf20Sopenharmony_ci (char *)skb->data, skb->len)) != 0) { 9168c2ecf20Sopenharmony_ci printk(KERN_ERR "fnic ctlr frame trace error!!!"); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci skb_queue_tail(&fnic->frame_queue, skb); 9208c2ecf20Sopenharmony_ci queue_work(fnic_event_queue, &fnic->frame_work); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_cidrop: 9248c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic int fnic_rq_cmpl_handler_cont(struct vnic_dev *vdev, 9288c2ecf20Sopenharmony_ci struct cq_desc *cq_desc, u8 type, 9298c2ecf20Sopenharmony_ci u16 q_number, u16 completed_index, 9308c2ecf20Sopenharmony_ci void *opaque) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(vdev); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci vnic_rq_service(&fnic->rq[q_number], cq_desc, completed_index, 9358c2ecf20Sopenharmony_ci VNIC_RQ_RETURN_DESC, fnic_rq_cmpl_frame_recv, 9368c2ecf20Sopenharmony_ci NULL); 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ciint fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci unsigned int tot_rq_work_done = 0, cur_work_done; 9438c2ecf20Sopenharmony_ci unsigned int i; 9448c2ecf20Sopenharmony_ci int err; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci for (i = 0; i < fnic->rq_count; i++) { 9478c2ecf20Sopenharmony_ci cur_work_done = vnic_cq_service(&fnic->cq[i], rq_work_to_do, 9488c2ecf20Sopenharmony_ci fnic_rq_cmpl_handler_cont, 9498c2ecf20Sopenharmony_ci NULL); 9508c2ecf20Sopenharmony_ci if (cur_work_done) { 9518c2ecf20Sopenharmony_ci err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame); 9528c2ecf20Sopenharmony_ci if (err) 9538c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, fnic->lport->host, 9548c2ecf20Sopenharmony_ci "fnic_alloc_rq_frame can't alloc" 9558c2ecf20Sopenharmony_ci " frame\n"); 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci tot_rq_work_done += cur_work_done; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return tot_rq_work_done; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* 9648c2ecf20Sopenharmony_ci * This function is called once at init time to allocate and fill RQ 9658c2ecf20Sopenharmony_ci * buffers. Subsequently, it is called in the interrupt context after RQ 9668c2ecf20Sopenharmony_ci * buffer processing to replenish the buffers in the RQ 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_ciint fnic_alloc_rq_frame(struct vnic_rq *rq) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(rq->vdev); 9718c2ecf20Sopenharmony_ci struct sk_buff *skb; 9728c2ecf20Sopenharmony_ci u16 len; 9738c2ecf20Sopenharmony_ci dma_addr_t pa; 9748c2ecf20Sopenharmony_ci int r; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM; 9778c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len); 9788c2ecf20Sopenharmony_ci if (!skb) { 9798c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 9808c2ecf20Sopenharmony_ci "Unable to allocate RQ sk_buff\n"); 9818c2ecf20Sopenharmony_ci return -ENOMEM; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 9848c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 9858c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 9868c2ecf20Sopenharmony_ci skb_put(skb, len); 9878c2ecf20Sopenharmony_ci pa = dma_map_single(&fnic->pdev->dev, skb->data, len, DMA_FROM_DEVICE); 9888c2ecf20Sopenharmony_ci if (dma_mapping_error(&fnic->pdev->dev, pa)) { 9898c2ecf20Sopenharmony_ci r = -ENOMEM; 9908c2ecf20Sopenharmony_ci printk(KERN_ERR "PCI mapping failed with error %d\n", r); 9918c2ecf20Sopenharmony_ci goto free_skb; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci fnic_queue_rq_desc(rq, skb, pa, len); 9958c2ecf20Sopenharmony_ci return 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cifree_skb: 9988c2ecf20Sopenharmony_ci kfree_skb(skb); 9998c2ecf20Sopenharmony_ci return r; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_civoid fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct fc_frame *fp = buf->os_buf; 10058c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(rq->vdev); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, 10088c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci dev_kfree_skb(fp_skb(fp)); 10118c2ecf20Sopenharmony_ci buf->os_buf = NULL; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci/** 10158c2ecf20Sopenharmony_ci * fnic_eth_send() - Send Ethernet frame. 10168c2ecf20Sopenharmony_ci * @fip: fcoe_ctlr instance. 10178c2ecf20Sopenharmony_ci * @skb: Ethernet Frame, FIP, without VLAN encapsulation. 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_civoid fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct fnic *fnic = fnic_from_ctlr(fip); 10228c2ecf20Sopenharmony_ci struct vnic_wq *wq = &fnic->wq[0]; 10238c2ecf20Sopenharmony_ci dma_addr_t pa; 10248c2ecf20Sopenharmony_ci struct ethhdr *eth_hdr; 10258c2ecf20Sopenharmony_ci struct vlan_ethhdr *vlan_hdr; 10268c2ecf20Sopenharmony_ci unsigned long flags; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (!fnic->vlan_hw_insert) { 10298c2ecf20Sopenharmony_ci eth_hdr = (struct ethhdr *)skb_mac_header(skb); 10308c2ecf20Sopenharmony_ci vlan_hdr = skb_push(skb, sizeof(*vlan_hdr) - sizeof(*eth_hdr)); 10318c2ecf20Sopenharmony_ci memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN); 10328c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q); 10338c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto; 10348c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id); 10358c2ecf20Sopenharmony_ci if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, 10368c2ecf20Sopenharmony_ci FNIC_FC_SEND|0x80, (char *)eth_hdr, skb->len)) != 0) { 10378c2ecf20Sopenharmony_ci printk(KERN_ERR "fnic ctlr frame trace error!!!"); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } else { 10408c2ecf20Sopenharmony_ci if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, 10418c2ecf20Sopenharmony_ci FNIC_FC_SEND|0x80, (char *)skb->data, skb->len)) != 0) { 10428c2ecf20Sopenharmony_ci printk(KERN_ERR "fnic ctlr frame trace error!!!"); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci pa = dma_map_single(&fnic->pdev->dev, skb->data, skb->len, 10478c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 10488c2ecf20Sopenharmony_ci if (dma_mapping_error(&fnic->pdev->dev, pa)) { 10498c2ecf20Sopenharmony_ci printk(KERN_ERR "DMA mapping failed\n"); 10508c2ecf20Sopenharmony_ci goto free_skb; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->wq_lock[0], flags); 10548c2ecf20Sopenharmony_ci if (!vnic_wq_desc_avail(wq)) 10558c2ecf20Sopenharmony_ci goto irq_restore; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci fnic_queue_wq_eth_desc(wq, skb, pa, skb->len, 10588c2ecf20Sopenharmony_ci 0 /* hw inserts cos value */, 10598c2ecf20Sopenharmony_ci fnic->vlan_id, 1); 10608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->wq_lock[0], flags); 10618c2ecf20Sopenharmony_ci return; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ciirq_restore: 10648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->wq_lock[0], flags); 10658c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, pa, skb->len, DMA_TO_DEVICE); 10668c2ecf20Sopenharmony_cifree_skb: 10678c2ecf20Sopenharmony_ci kfree_skb(skb); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci/* 10718c2ecf20Sopenharmony_ci * Send FC frame. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct vnic_wq *wq = &fnic->wq[0]; 10768c2ecf20Sopenharmony_ci struct sk_buff *skb; 10778c2ecf20Sopenharmony_ci dma_addr_t pa; 10788c2ecf20Sopenharmony_ci struct ethhdr *eth_hdr; 10798c2ecf20Sopenharmony_ci struct vlan_ethhdr *vlan_hdr; 10808c2ecf20Sopenharmony_ci struct fcoe_hdr *fcoe_hdr; 10818c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 10828c2ecf20Sopenharmony_ci u32 tot_len, eth_hdr_len; 10838c2ecf20Sopenharmony_ci int ret = 0; 10848c2ecf20Sopenharmony_ci unsigned long flags; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 10878c2ecf20Sopenharmony_ci skb = fp_skb(fp); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && 10908c2ecf20Sopenharmony_ci fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb)) 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (!fnic->vlan_hw_insert) { 10948c2ecf20Sopenharmony_ci eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr); 10958c2ecf20Sopenharmony_ci vlan_hdr = skb_push(skb, eth_hdr_len); 10968c2ecf20Sopenharmony_ci eth_hdr = (struct ethhdr *)vlan_hdr; 10978c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q); 10988c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_encapsulated_proto = htons(ETH_P_FCOE); 10998c2ecf20Sopenharmony_ci vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id); 11008c2ecf20Sopenharmony_ci fcoe_hdr = (struct fcoe_hdr *)(vlan_hdr + 1); 11018c2ecf20Sopenharmony_ci } else { 11028c2ecf20Sopenharmony_ci eth_hdr_len = sizeof(*eth_hdr) + sizeof(*fcoe_hdr); 11038c2ecf20Sopenharmony_ci eth_hdr = skb_push(skb, eth_hdr_len); 11048c2ecf20Sopenharmony_ci eth_hdr->h_proto = htons(ETH_P_FCOE); 11058c2ecf20Sopenharmony_ci fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1); 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (fnic->ctlr.map_dest) 11098c2ecf20Sopenharmony_ci fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id); 11108c2ecf20Sopenharmony_ci else 11118c2ecf20Sopenharmony_ci memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN); 11128c2ecf20Sopenharmony_ci memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci tot_len = skb->len; 11158c2ecf20Sopenharmony_ci BUG_ON(tot_len % 4); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci memset(fcoe_hdr, 0, sizeof(*fcoe_hdr)); 11188c2ecf20Sopenharmony_ci fcoe_hdr->fcoe_sof = fr_sof(fp); 11198c2ecf20Sopenharmony_ci if (FC_FCOE_VER) 11208c2ecf20Sopenharmony_ci FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci pa = dma_map_single(&fnic->pdev->dev, eth_hdr, tot_len, DMA_TO_DEVICE); 11238c2ecf20Sopenharmony_ci if (dma_mapping_error(&fnic->pdev->dev, pa)) { 11248c2ecf20Sopenharmony_ci ret = -ENOMEM; 11258c2ecf20Sopenharmony_ci printk(KERN_ERR "DMA map failed with error %d\n", ret); 11268c2ecf20Sopenharmony_ci goto free_skb_on_err; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if ((fnic_fc_trace_set_data(fnic->lport->host->host_no, FNIC_FC_SEND, 11308c2ecf20Sopenharmony_ci (char *)eth_hdr, tot_len)) != 0) { 11318c2ecf20Sopenharmony_ci printk(KERN_ERR "fnic ctlr frame trace error!!!"); 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->wq_lock[0], flags); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (!vnic_wq_desc_avail(wq)) { 11378c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, pa, tot_len, DMA_TO_DEVICE); 11388c2ecf20Sopenharmony_ci ret = -1; 11398c2ecf20Sopenharmony_ci goto irq_restore; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp), 11438c2ecf20Sopenharmony_ci 0 /* hw inserts cos value */, 11448c2ecf20Sopenharmony_ci fnic->vlan_id, 1, 1, 1); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ciirq_restore: 11478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->wq_lock[0], flags); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cifree_skb_on_err: 11508c2ecf20Sopenharmony_ci if (ret) 11518c2ecf20Sopenharmony_ci dev_kfree_skb_any(fp_skb(fp)); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci return ret; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci/* 11578c2ecf20Sopenharmony_ci * fnic_send 11588c2ecf20Sopenharmony_ci * Routine to send a raw frame 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_ciint fnic_send(struct fc_lport *lp, struct fc_frame *fp) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct fnic *fnic = lport_priv(lp); 11638c2ecf20Sopenharmony_ci unsigned long flags; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (fnic->in_remove) { 11668c2ecf20Sopenharmony_ci dev_kfree_skb(fp_skb(fp)); 11678c2ecf20Sopenharmony_ci return -1; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* 11718c2ecf20Sopenharmony_ci * Queue frame if in a transitional state. 11728c2ecf20Sopenharmony_ci * This occurs while registering the Port_ID / MAC address after FLOGI. 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 11758c2ecf20Sopenharmony_ci if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) { 11768c2ecf20Sopenharmony_ci skb_queue_tail(&fnic->tx_queue, fp_skb(fp)); 11778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return fnic_send_frame(fnic, fp); 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci/** 11868c2ecf20Sopenharmony_ci * fnic_flush_tx() - send queued frames. 11878c2ecf20Sopenharmony_ci * @fnic: fnic device 11888c2ecf20Sopenharmony_ci * 11898c2ecf20Sopenharmony_ci * Send frames that were waiting to go out in FC or Ethernet mode. 11908c2ecf20Sopenharmony_ci * Whenever changing modes we purge queued frames, so these frames should 11918c2ecf20Sopenharmony_ci * be queued for the stable mode that we're in, either FC or Ethernet. 11928c2ecf20Sopenharmony_ci * 11938c2ecf20Sopenharmony_ci * Called without fnic_lock held. 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_civoid fnic_flush_tx(struct fnic *fnic) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct sk_buff *skb; 11988c2ecf20Sopenharmony_ci struct fc_frame *fp; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&fnic->tx_queue))) { 12018c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 12028c2ecf20Sopenharmony_ci fnic_send_frame(fnic, fp); 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci/** 12078c2ecf20Sopenharmony_ci * fnic_set_eth_mode() - put fnic into ethernet mode. 12088c2ecf20Sopenharmony_ci * @fnic: fnic device 12098c2ecf20Sopenharmony_ci * 12108c2ecf20Sopenharmony_ci * Called without fnic lock held. 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_cistatic void fnic_set_eth_mode(struct fnic *fnic) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci unsigned long flags; 12158c2ecf20Sopenharmony_ci enum fnic_state old_state; 12168c2ecf20Sopenharmony_ci int ret; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 12198c2ecf20Sopenharmony_ciagain: 12208c2ecf20Sopenharmony_ci old_state = fnic->state; 12218c2ecf20Sopenharmony_ci switch (old_state) { 12228c2ecf20Sopenharmony_ci case FNIC_IN_FC_MODE: 12238c2ecf20Sopenharmony_ci case FNIC_IN_ETH_TRANS_FC_MODE: 12248c2ecf20Sopenharmony_ci default: 12258c2ecf20Sopenharmony_ci fnic->state = FNIC_IN_FC_TRANS_ETH_MODE; 12268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci ret = fnic_fw_reset_handler(fnic); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 12318c2ecf20Sopenharmony_ci if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE) 12328c2ecf20Sopenharmony_ci goto again; 12338c2ecf20Sopenharmony_ci if (ret) 12348c2ecf20Sopenharmony_ci fnic->state = old_state; 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci case FNIC_IN_FC_TRANS_ETH_MODE: 12388c2ecf20Sopenharmony_ci case FNIC_IN_ETH_MODE: 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic void fnic_wq_complete_frame_send(struct vnic_wq *wq, 12458c2ecf20Sopenharmony_ci struct cq_desc *cq_desc, 12468c2ecf20Sopenharmony_ci struct vnic_wq_buf *buf, void *opaque) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct sk_buff *skb = buf->os_buf; 12498c2ecf20Sopenharmony_ci struct fc_frame *fp = (struct fc_frame *)skb; 12508c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(wq->vdev); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, 12538c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12548c2ecf20Sopenharmony_ci dev_kfree_skb_irq(fp_skb(fp)); 12558c2ecf20Sopenharmony_ci buf->os_buf = NULL; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic int fnic_wq_cmpl_handler_cont(struct vnic_dev *vdev, 12598c2ecf20Sopenharmony_ci struct cq_desc *cq_desc, u8 type, 12608c2ecf20Sopenharmony_ci u16 q_number, u16 completed_index, 12618c2ecf20Sopenharmony_ci void *opaque) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(vdev); 12648c2ecf20Sopenharmony_ci unsigned long flags; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->wq_lock[q_number], flags); 12678c2ecf20Sopenharmony_ci vnic_wq_service(&fnic->wq[q_number], cq_desc, completed_index, 12688c2ecf20Sopenharmony_ci fnic_wq_complete_frame_send, NULL); 12698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->wq_lock[q_number], flags); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci return 0; 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ciint fnic_wq_cmpl_handler(struct fnic *fnic, int work_to_do) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci unsigned int wq_work_done = 0; 12778c2ecf20Sopenharmony_ci unsigned int i; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci for (i = 0; i < fnic->raw_wq_count; i++) { 12808c2ecf20Sopenharmony_ci wq_work_done += vnic_cq_service(&fnic->cq[fnic->rq_count+i], 12818c2ecf20Sopenharmony_ci work_to_do, 12828c2ecf20Sopenharmony_ci fnic_wq_cmpl_handler_cont, 12838c2ecf20Sopenharmony_ci NULL); 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci return wq_work_done; 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_civoid fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct fc_frame *fp = buf->os_buf; 12938c2ecf20Sopenharmony_ci struct fnic *fnic = vnic_dev_priv(wq->vdev); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, 12968c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci dev_kfree_skb(fp_skb(fp)); 12998c2ecf20Sopenharmony_ci buf->os_buf = NULL; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_civoid fnic_fcoe_reset_vlans(struct fnic *fnic) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci unsigned long flags; 13058c2ecf20Sopenharmony_ci struct fcoe_vlan *vlan; 13068c2ecf20Sopenharmony_ci struct fcoe_vlan *next; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* 13098c2ecf20Sopenharmony_ci * indicate a link down to fcoe so that all fcf's are free'd 13108c2ecf20Sopenharmony_ci * might not be required since we did this before sending vlan 13118c2ecf20Sopenharmony_ci * discovery request 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->vlans_lock, flags); 13148c2ecf20Sopenharmony_ci if (!list_empty(&fnic->vlans)) { 13158c2ecf20Sopenharmony_ci list_for_each_entry_safe(vlan, next, &fnic->vlans, list) { 13168c2ecf20Sopenharmony_ci list_del(&vlan->list); 13178c2ecf20Sopenharmony_ci kfree(vlan); 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_civoid fnic_handle_fip_timer(struct fnic *fnic) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci unsigned long flags; 13268c2ecf20Sopenharmony_ci struct fcoe_vlan *vlan; 13278c2ecf20Sopenharmony_ci struct fnic_stats *fnic_stats = &fnic->fnic_stats; 13288c2ecf20Sopenharmony_ci u64 sol_time; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->fnic_lock, flags); 13318c2ecf20Sopenharmony_ci if (fnic->stop_rx_link_events) { 13328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 13338c2ecf20Sopenharmony_ci return; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->fnic_lock, flags); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (fnic->ctlr.mode == FIP_MODE_NON_FIP) 13388c2ecf20Sopenharmony_ci return; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci spin_lock_irqsave(&fnic->vlans_lock, flags); 13418c2ecf20Sopenharmony_ci if (list_empty(&fnic->vlans)) { 13428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 13438c2ecf20Sopenharmony_ci /* no vlans available, try again */ 13448c2ecf20Sopenharmony_ci if (printk_ratelimit()) 13458c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 13468c2ecf20Sopenharmony_ci "Start VLAN Discovery\n"); 13478c2ecf20Sopenharmony_ci fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC); 13488c2ecf20Sopenharmony_ci return; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, list); 13528c2ecf20Sopenharmony_ci shost_printk(KERN_DEBUG, fnic->lport->host, 13538c2ecf20Sopenharmony_ci "fip_timer: vlan %d state %d sol_count %d\n", 13548c2ecf20Sopenharmony_ci vlan->vid, vlan->state, vlan->sol_count); 13558c2ecf20Sopenharmony_ci switch (vlan->state) { 13568c2ecf20Sopenharmony_ci case FIP_VLAN_USED: 13578c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 13588c2ecf20Sopenharmony_ci "FIP VLAN is selected for FC transaction\n"); 13598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci case FIP_VLAN_FAILED: 13628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 13638c2ecf20Sopenharmony_ci /* if all vlans are in failed state, restart vlan disc */ 13648c2ecf20Sopenharmony_ci if (printk_ratelimit()) 13658c2ecf20Sopenharmony_ci FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, 13668c2ecf20Sopenharmony_ci "Start VLAN Discovery\n"); 13678c2ecf20Sopenharmony_ci fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC); 13688c2ecf20Sopenharmony_ci break; 13698c2ecf20Sopenharmony_ci case FIP_VLAN_SENT: 13708c2ecf20Sopenharmony_ci if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) { 13718c2ecf20Sopenharmony_ci /* 13728c2ecf20Sopenharmony_ci * no response on this vlan, remove from the list. 13738c2ecf20Sopenharmony_ci * Try the next vlan 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 13768c2ecf20Sopenharmony_ci "Dequeue this VLAN ID %d from list\n", 13778c2ecf20Sopenharmony_ci vlan->vid); 13788c2ecf20Sopenharmony_ci list_del(&vlan->list); 13798c2ecf20Sopenharmony_ci kfree(vlan); 13808c2ecf20Sopenharmony_ci vlan = NULL; 13818c2ecf20Sopenharmony_ci if (list_empty(&fnic->vlans)) { 13828c2ecf20Sopenharmony_ci /* we exhausted all vlans, restart vlan disc */ 13838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, 13848c2ecf20Sopenharmony_ci flags); 13858c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, fnic->lport->host, 13868c2ecf20Sopenharmony_ci "fip_timer: vlan list empty, " 13878c2ecf20Sopenharmony_ci "trigger vlan disc\n"); 13888c2ecf20Sopenharmony_ci fnic_event_enq(fnic, FNIC_EVT_START_VLAN_DISC); 13898c2ecf20Sopenharmony_ci return; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci /* check the next vlan */ 13928c2ecf20Sopenharmony_ci vlan = list_first_entry(&fnic->vlans, struct fcoe_vlan, 13938c2ecf20Sopenharmony_ci list); 13948c2ecf20Sopenharmony_ci fnic->set_vlan(fnic, vlan->vid); 13958c2ecf20Sopenharmony_ci vlan->state = FIP_VLAN_SENT; /* sent now */ 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&fnic->vlans_lock, flags); 13988c2ecf20Sopenharmony_ci atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count); 13998c2ecf20Sopenharmony_ci vlan->sol_count++; 14008c2ecf20Sopenharmony_ci sol_time = jiffies + msecs_to_jiffies 14018c2ecf20Sopenharmony_ci (FCOE_CTLR_START_DELAY); 14028c2ecf20Sopenharmony_ci mod_timer(&fnic->fip_timer, round_jiffies(sol_time)); 14038c2ecf20Sopenharmony_ci break; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci} 1406