18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Maintained at www.Open-FCoE.org 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 138c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 148c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 158c2ecf20Sopenharmony_ci#include <linux/crc32.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/cpu.h> 188c2ecf20Sopenharmony_ci#include <linux/fs.h> 198c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/ctype.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci#include <net/dcbnl.h> 238c2ecf20Sopenharmony_ci#include <net/dcbevent.h> 248c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 258c2ecf20Sopenharmony_ci#include <scsi/scsicam.h> 268c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h> 278c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_fc.h> 288c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <scsi/fc/fc_encaps.h> 318c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fip.h> 328c2ecf20Sopenharmony_ci#include <scsi/fc/fc_fcoe.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <scsi/libfc.h> 358c2ecf20Sopenharmony_ci#include <scsi/fc_frame.h> 368c2ecf20Sopenharmony_ci#include <scsi/libfcoe.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "fcoe.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Open-FCoE.org"); 418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FCoE"); 428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Performance tuning parameters for fcoe */ 458c2ecf20Sopenharmony_cistatic unsigned int fcoe_ddp_min = 4096; 468c2ecf20Sopenharmony_cimodule_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ 488c2ecf20Sopenharmony_ci "Direct Data Placement (DDP)."); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciunsigned int fcoe_debug_logging; 518c2ecf20Sopenharmony_cimodule_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic unsigned int fcoe_e_d_tov = 2 * 1000; 558c2ecf20Sopenharmony_cimodule_param_named(e_d_tov, fcoe_e_d_tov, int, S_IRUGO|S_IWUSR); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(e_d_tov, "E_D_TOV in ms, default 2000"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic unsigned int fcoe_r_a_tov = 2 * 2 * 1000; 598c2ecf20Sopenharmony_cimodule_param_named(r_a_tov, fcoe_r_a_tov, int, S_IRUGO|S_IWUSR); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(r_a_tov, "R_A_TOV in ms, default 4000"); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fcoe_config_mutex); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct workqueue_struct *fcoe_wq; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* fcoe host list */ 678c2ecf20Sopenharmony_ci/* must only by accessed under the RTNL mutex */ 688c2ecf20Sopenharmony_cistatic LIST_HEAD(fcoe_hostlist); 698c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* Function Prototypes */ 728c2ecf20Sopenharmony_cistatic int fcoe_reset(struct Scsi_Host *); 738c2ecf20Sopenharmony_cistatic int fcoe_xmit(struct fc_lport *, struct fc_frame *); 748c2ecf20Sopenharmony_cistatic int fcoe_rcv(struct sk_buff *, struct net_device *, 758c2ecf20Sopenharmony_ci struct packet_type *, struct net_device *); 768c2ecf20Sopenharmony_cistatic void fcoe_percpu_clean(struct fc_lport *); 778c2ecf20Sopenharmony_cistatic int fcoe_link_ok(struct fc_lport *); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); 808c2ecf20Sopenharmony_cistatic int fcoe_hostlist_add(const struct fc_lport *); 818c2ecf20Sopenharmony_cistatic void fcoe_hostlist_del(const struct fc_lport *); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int fcoe_device_notification(struct notifier_block *, ulong, void *); 848c2ecf20Sopenharmony_cistatic void fcoe_dev_setup(void); 858c2ecf20Sopenharmony_cistatic void fcoe_dev_cleanup(void); 868c2ecf20Sopenharmony_cistatic struct fcoe_interface 878c2ecf20Sopenharmony_ci*fcoe_hostlist_lookup_port(const struct net_device *); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int fcoe_fip_recv(struct sk_buff *, struct net_device *, 908c2ecf20Sopenharmony_ci struct packet_type *, struct net_device *); 918c2ecf20Sopenharmony_cistatic int fcoe_fip_vlan_recv(struct sk_buff *, struct net_device *, 928c2ecf20Sopenharmony_ci struct packet_type *, struct net_device *); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *); 958c2ecf20Sopenharmony_cistatic void fcoe_update_src_mac(struct fc_lport *, u8 *); 968c2ecf20Sopenharmony_cistatic u8 *fcoe_get_src_mac(struct fc_lport *); 978c2ecf20Sopenharmony_cistatic void fcoe_destroy_work(struct work_struct *); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *, 1008c2ecf20Sopenharmony_ci unsigned int); 1018c2ecf20Sopenharmony_cistatic int fcoe_ddp_done(struct fc_lport *, u16); 1028c2ecf20Sopenharmony_cistatic int fcoe_ddp_target(struct fc_lport *, u16, struct scatterlist *, 1038c2ecf20Sopenharmony_ci unsigned int); 1048c2ecf20Sopenharmony_cistatic int fcoe_dcb_app_notification(struct notifier_block *notifier, 1058c2ecf20Sopenharmony_ci ulong event, void *ptr); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic bool fcoe_match(struct net_device *netdev); 1088c2ecf20Sopenharmony_cistatic int fcoe_create(struct net_device *netdev, enum fip_mode fip_mode); 1098c2ecf20Sopenharmony_cistatic int fcoe_destroy(struct net_device *netdev); 1108c2ecf20Sopenharmony_cistatic int fcoe_enable(struct net_device *netdev); 1118c2ecf20Sopenharmony_cistatic int fcoe_disable(struct net_device *netdev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* fcoe_syfs control interface handlers */ 1148c2ecf20Sopenharmony_cistatic int fcoe_ctlr_alloc(struct net_device *netdev); 1158c2ecf20Sopenharmony_cistatic int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev); 1168c2ecf20Sopenharmony_cistatic void fcoe_ctlr_mode(struct fcoe_ctlr_device *ctlr_dev); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct fc_seq *fcoe_elsct_send(struct fc_lport *, 1198c2ecf20Sopenharmony_ci u32 did, struct fc_frame *, 1208c2ecf20Sopenharmony_ci unsigned int op, 1218c2ecf20Sopenharmony_ci void (*resp)(struct fc_seq *, 1228c2ecf20Sopenharmony_ci struct fc_frame *, 1238c2ecf20Sopenharmony_ci void *), 1248c2ecf20Sopenharmony_ci void *, u32 timeout); 1258c2ecf20Sopenharmony_cistatic void fcoe_recv_frame(struct sk_buff *skb); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* notification function for packets from net device */ 1288c2ecf20Sopenharmony_cistatic struct notifier_block fcoe_notifier = { 1298c2ecf20Sopenharmony_ci .notifier_call = fcoe_device_notification, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* notification function for DCB events */ 1338c2ecf20Sopenharmony_cistatic struct notifier_block dcb_notifier = { 1348c2ecf20Sopenharmony_ci .notifier_call = fcoe_dcb_app_notification, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic struct scsi_transport_template *fcoe_nport_scsi_transport; 1388c2ecf20Sopenharmony_cistatic struct scsi_transport_template *fcoe_vport_scsi_transport; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int fcoe_vport_destroy(struct fc_vport *); 1418c2ecf20Sopenharmony_cistatic int fcoe_vport_create(struct fc_vport *, bool disabled); 1428c2ecf20Sopenharmony_cistatic int fcoe_vport_disable(struct fc_vport *, bool disable); 1438c2ecf20Sopenharmony_cistatic void fcoe_set_vport_symbolic_name(struct fc_vport *); 1448c2ecf20Sopenharmony_cistatic void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); 1458c2ecf20Sopenharmony_cistatic void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); 1468c2ecf20Sopenharmony_cistatic void fcoe_vport_remove(struct fc_lport *); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct fcoe_sysfs_function_template fcoe_sysfs_templ = { 1498c2ecf20Sopenharmony_ci .set_fcoe_ctlr_mode = fcoe_ctlr_mode, 1508c2ecf20Sopenharmony_ci .set_fcoe_ctlr_enabled = fcoe_ctlr_enabled, 1518c2ecf20Sopenharmony_ci .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, 1528c2ecf20Sopenharmony_ci .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, 1538c2ecf20Sopenharmony_ci .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, 1548c2ecf20Sopenharmony_ci .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, 1558c2ecf20Sopenharmony_ci .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, 1568c2ecf20Sopenharmony_ci .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci .get_fcoe_fcf_selected = fcoe_fcf_get_selected, 1598c2ecf20Sopenharmony_ci .get_fcoe_fcf_vlan_id = fcoe_fcf_get_vlan_id, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct libfc_function_template fcoe_libfc_fcn_templ = { 1638c2ecf20Sopenharmony_ci .frame_send = fcoe_xmit, 1648c2ecf20Sopenharmony_ci .ddp_setup = fcoe_ddp_setup, 1658c2ecf20Sopenharmony_ci .ddp_done = fcoe_ddp_done, 1668c2ecf20Sopenharmony_ci .ddp_target = fcoe_ddp_target, 1678c2ecf20Sopenharmony_ci .elsct_send = fcoe_elsct_send, 1688c2ecf20Sopenharmony_ci .get_lesb = fcoe_get_lesb, 1698c2ecf20Sopenharmony_ci .lport_set_port_id = fcoe_set_port_id, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic struct fc_function_template fcoe_nport_fc_functions = { 1738c2ecf20Sopenharmony_ci .show_host_node_name = 1, 1748c2ecf20Sopenharmony_ci .show_host_port_name = 1, 1758c2ecf20Sopenharmony_ci .show_host_supported_classes = 1, 1768c2ecf20Sopenharmony_ci .show_host_supported_fc4s = 1, 1778c2ecf20Sopenharmony_ci .show_host_active_fc4s = 1, 1788c2ecf20Sopenharmony_ci .show_host_maxframe_size = 1, 1798c2ecf20Sopenharmony_ci .show_host_serial_number = 1, 1808c2ecf20Sopenharmony_ci .show_host_manufacturer = 1, 1818c2ecf20Sopenharmony_ci .show_host_model = 1, 1828c2ecf20Sopenharmony_ci .show_host_model_description = 1, 1838c2ecf20Sopenharmony_ci .show_host_hardware_version = 1, 1848c2ecf20Sopenharmony_ci .show_host_driver_version = 1, 1858c2ecf20Sopenharmony_ci .show_host_firmware_version = 1, 1868c2ecf20Sopenharmony_ci .show_host_optionrom_version = 1, 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci .show_host_port_id = 1, 1898c2ecf20Sopenharmony_ci .show_host_supported_speeds = 1, 1908c2ecf20Sopenharmony_ci .get_host_speed = fc_get_host_speed, 1918c2ecf20Sopenharmony_ci .show_host_speed = 1, 1928c2ecf20Sopenharmony_ci .show_host_port_type = 1, 1938c2ecf20Sopenharmony_ci .get_host_port_state = fc_get_host_port_state, 1948c2ecf20Sopenharmony_ci .show_host_port_state = 1, 1958c2ecf20Sopenharmony_ci .show_host_symbolic_name = 1, 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), 1988c2ecf20Sopenharmony_ci .show_rport_maxframe_size = 1, 1998c2ecf20Sopenharmony_ci .show_rport_supported_classes = 1, 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci .show_host_fabric_name = 1, 2028c2ecf20Sopenharmony_ci .show_starget_node_name = 1, 2038c2ecf20Sopenharmony_ci .show_starget_port_name = 1, 2048c2ecf20Sopenharmony_ci .show_starget_port_id = 1, 2058c2ecf20Sopenharmony_ci .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, 2068c2ecf20Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 2078c2ecf20Sopenharmony_ci .get_fc_host_stats = fc_get_host_stats, 2088c2ecf20Sopenharmony_ci .issue_fc_host_lip = fcoe_reset, 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci .terminate_rport_io = fc_rport_terminate_io, 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci .vport_create = fcoe_vport_create, 2138c2ecf20Sopenharmony_ci .vport_delete = fcoe_vport_destroy, 2148c2ecf20Sopenharmony_ci .vport_disable = fcoe_vport_disable, 2158c2ecf20Sopenharmony_ci .set_vport_symbolic_name = fcoe_set_vport_symbolic_name, 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci .bsg_request = fc_lport_bsg_request, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic struct fc_function_template fcoe_vport_fc_functions = { 2218c2ecf20Sopenharmony_ci .show_host_node_name = 1, 2228c2ecf20Sopenharmony_ci .show_host_port_name = 1, 2238c2ecf20Sopenharmony_ci .show_host_supported_classes = 1, 2248c2ecf20Sopenharmony_ci .show_host_supported_fc4s = 1, 2258c2ecf20Sopenharmony_ci .show_host_active_fc4s = 1, 2268c2ecf20Sopenharmony_ci .show_host_maxframe_size = 1, 2278c2ecf20Sopenharmony_ci .show_host_serial_number = 1, 2288c2ecf20Sopenharmony_ci .show_host_manufacturer = 1, 2298c2ecf20Sopenharmony_ci .show_host_model = 1, 2308c2ecf20Sopenharmony_ci .show_host_model_description = 1, 2318c2ecf20Sopenharmony_ci .show_host_hardware_version = 1, 2328c2ecf20Sopenharmony_ci .show_host_driver_version = 1, 2338c2ecf20Sopenharmony_ci .show_host_firmware_version = 1, 2348c2ecf20Sopenharmony_ci .show_host_optionrom_version = 1, 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci .show_host_port_id = 1, 2378c2ecf20Sopenharmony_ci .show_host_supported_speeds = 1, 2388c2ecf20Sopenharmony_ci .get_host_speed = fc_get_host_speed, 2398c2ecf20Sopenharmony_ci .show_host_speed = 1, 2408c2ecf20Sopenharmony_ci .show_host_port_type = 1, 2418c2ecf20Sopenharmony_ci .get_host_port_state = fc_get_host_port_state, 2428c2ecf20Sopenharmony_ci .show_host_port_state = 1, 2438c2ecf20Sopenharmony_ci .show_host_symbolic_name = 1, 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), 2468c2ecf20Sopenharmony_ci .show_rport_maxframe_size = 1, 2478c2ecf20Sopenharmony_ci .show_rport_supported_classes = 1, 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci .show_host_fabric_name = 1, 2508c2ecf20Sopenharmony_ci .show_starget_node_name = 1, 2518c2ecf20Sopenharmony_ci .show_starget_port_name = 1, 2528c2ecf20Sopenharmony_ci .show_starget_port_id = 1, 2538c2ecf20Sopenharmony_ci .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, 2548c2ecf20Sopenharmony_ci .show_rport_dev_loss_tmo = 1, 2558c2ecf20Sopenharmony_ci .get_fc_host_stats = fc_get_host_stats, 2568c2ecf20Sopenharmony_ci .issue_fc_host_lip = fcoe_reset, 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci .terminate_rport_io = fc_rport_terminate_io, 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci .bsg_request = fc_lport_bsg_request, 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic struct scsi_host_template fcoe_shost_template = { 2648c2ecf20Sopenharmony_ci .module = THIS_MODULE, 2658c2ecf20Sopenharmony_ci .name = "FCoE Driver", 2668c2ecf20Sopenharmony_ci .proc_name = FCOE_NAME, 2678c2ecf20Sopenharmony_ci .queuecommand = fc_queuecommand, 2688c2ecf20Sopenharmony_ci .eh_timed_out = fc_eh_timed_out, 2698c2ecf20Sopenharmony_ci .eh_abort_handler = fc_eh_abort, 2708c2ecf20Sopenharmony_ci .eh_device_reset_handler = fc_eh_device_reset, 2718c2ecf20Sopenharmony_ci .eh_host_reset_handler = fc_eh_host_reset, 2728c2ecf20Sopenharmony_ci .slave_alloc = fc_slave_alloc, 2738c2ecf20Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 2748c2ecf20Sopenharmony_ci .this_id = -1, 2758c2ecf20Sopenharmony_ci .cmd_per_lun = 3, 2768c2ecf20Sopenharmony_ci .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS, 2778c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 2788c2ecf20Sopenharmony_ci .max_sectors = 0xffff, 2798c2ecf20Sopenharmony_ci .track_queue_depth = 1, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/** 2838c2ecf20Sopenharmony_ci * fcoe_interface_setup() - Setup a FCoE interface 2848c2ecf20Sopenharmony_ci * @fcoe: The new FCoE interface 2858c2ecf20Sopenharmony_ci * @netdev: The net device that the fcoe interface is on 2868c2ecf20Sopenharmony_ci * 2878c2ecf20Sopenharmony_ci * Returns : 0 for success 2888c2ecf20Sopenharmony_ci * Locking: must be called with the RTNL mutex held 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_cistatic int fcoe_interface_setup(struct fcoe_interface *fcoe, 2918c2ecf20Sopenharmony_ci struct net_device *netdev) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); 2948c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 2958c2ecf20Sopenharmony_ci struct net_device *real_dev; 2968c2ecf20Sopenharmony_ci u8 flogi_maddr[ETH_ALEN]; 2978c2ecf20Sopenharmony_ci const struct net_device_ops *ops; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci fcoe->netdev = netdev; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Let LLD initialize for FCoE */ 3028c2ecf20Sopenharmony_ci ops = netdev->netdev_ops; 3038c2ecf20Sopenharmony_ci if (ops->ndo_fcoe_enable) { 3048c2ecf20Sopenharmony_ci if (ops->ndo_fcoe_enable(netdev)) 3058c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE" 3068c2ecf20Sopenharmony_ci " specific feature for LLD.\n"); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Do not support for bonding device */ 3108c2ecf20Sopenharmony_ci if (netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER) { 3118c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n"); 3128c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* look for SAN MAC address, if multiple SAN MACs exist, only 3168c2ecf20Sopenharmony_ci * use the first one for SPMA */ 3178c2ecf20Sopenharmony_ci real_dev = is_vlan_dev(netdev) ? vlan_dev_real_dev(netdev) : netdev; 3188c2ecf20Sopenharmony_ci fcoe->realdev = real_dev; 3198c2ecf20Sopenharmony_ci rcu_read_lock(); 3208c2ecf20Sopenharmony_ci for_each_dev_addr(real_dev, ha) { 3218c2ecf20Sopenharmony_ci if ((ha->type == NETDEV_HW_ADDR_T_SAN) && 3228c2ecf20Sopenharmony_ci (is_valid_ether_addr(ha->addr))) { 3238c2ecf20Sopenharmony_ci memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); 3248c2ecf20Sopenharmony_ci fip->spma = 1; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci rcu_read_unlock(); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* setup Source Mac Address */ 3318c2ecf20Sopenharmony_ci if (!fip->spma) 3328c2ecf20Sopenharmony_ci memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* 3358c2ecf20Sopenharmony_ci * Add FCoE MAC address as second unicast MAC address 3368c2ecf20Sopenharmony_ci * or enter promiscuous mode if not capable of listening 3378c2ecf20Sopenharmony_ci * for multiple unicast MACs. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); 3408c2ecf20Sopenharmony_ci dev_uc_add(netdev, flogi_maddr); 3418c2ecf20Sopenharmony_ci if (fip->spma) 3428c2ecf20Sopenharmony_ci dev_uc_add(netdev, fip->ctl_src_addr); 3438c2ecf20Sopenharmony_ci if (fip->mode == FIP_MODE_VN2VN) { 3448c2ecf20Sopenharmony_ci dev_mc_add(netdev, FIP_ALL_VN2VN_MACS); 3458c2ecf20Sopenharmony_ci dev_mc_add(netdev, FIP_ALL_P2P_MACS); 3468c2ecf20Sopenharmony_ci } else 3478c2ecf20Sopenharmony_ci dev_mc_add(netdev, FIP_ALL_ENODE_MACS); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* 3508c2ecf20Sopenharmony_ci * setup the receive function from ethernet driver 3518c2ecf20Sopenharmony_ci * on the ethertype for the given device 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci fcoe->fcoe_packet_type.func = fcoe_rcv; 3548c2ecf20Sopenharmony_ci fcoe->fcoe_packet_type.type = htons(ETH_P_FCOE); 3558c2ecf20Sopenharmony_ci fcoe->fcoe_packet_type.dev = netdev; 3568c2ecf20Sopenharmony_ci dev_add_pack(&fcoe->fcoe_packet_type); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci fcoe->fip_packet_type.func = fcoe_fip_recv; 3598c2ecf20Sopenharmony_ci fcoe->fip_packet_type.type = htons(ETH_P_FIP); 3608c2ecf20Sopenharmony_ci fcoe->fip_packet_type.dev = netdev; 3618c2ecf20Sopenharmony_ci dev_add_pack(&fcoe->fip_packet_type); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (netdev != real_dev) { 3648c2ecf20Sopenharmony_ci fcoe->fip_vlan_packet_type.func = fcoe_fip_vlan_recv; 3658c2ecf20Sopenharmony_ci fcoe->fip_vlan_packet_type.type = htons(ETH_P_FIP); 3668c2ecf20Sopenharmony_ci fcoe->fip_vlan_packet_type.dev = real_dev; 3678c2ecf20Sopenharmony_ci dev_add_pack(&fcoe->fip_vlan_packet_type); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/** 3738c2ecf20Sopenharmony_ci * fcoe_interface_create() - Create a FCoE interface on a net device 3748c2ecf20Sopenharmony_ci * @netdev: The net device to create the FCoE interface on 3758c2ecf20Sopenharmony_ci * @fip_mode: The mode to use for FIP 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Returns: pointer to a struct fcoe_interface or NULL on error 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_cistatic struct fcoe_interface *fcoe_interface_create(struct net_device *netdev, 3808c2ecf20Sopenharmony_ci enum fip_mode fip_mode) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *ctlr_dev; 3838c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 3848c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 3858c2ecf20Sopenharmony_ci int size; 3868c2ecf20Sopenharmony_ci int err; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) { 3898c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, 3908c2ecf20Sopenharmony_ci "Could not get a reference to the module\n"); 3918c2ecf20Sopenharmony_ci fcoe = ERR_PTR(-EBUSY); 3928c2ecf20Sopenharmony_ci goto out; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci size = sizeof(struct fcoe_ctlr) + sizeof(struct fcoe_interface); 3968c2ecf20Sopenharmony_ci ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &fcoe_sysfs_templ, 3978c2ecf20Sopenharmony_ci size); 3988c2ecf20Sopenharmony_ci if (!ctlr_dev) { 3998c2ecf20Sopenharmony_ci FCOE_DBG("Failed to add fcoe_ctlr_device\n"); 4008c2ecf20Sopenharmony_ci fcoe = ERR_PTR(-ENOMEM); 4018c2ecf20Sopenharmony_ci goto out_putmod; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ctlr = fcoe_ctlr_device_priv(ctlr_dev); 4058c2ecf20Sopenharmony_ci ctlr->cdev = ctlr_dev; 4068c2ecf20Sopenharmony_ci fcoe = fcoe_ctlr_priv(ctlr); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dev_hold(netdev); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* 4118c2ecf20Sopenharmony_ci * Initialize FIP. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci fcoe_ctlr_init(ctlr, fip_mode); 4148c2ecf20Sopenharmony_ci ctlr->send = fcoe_fip_send; 4158c2ecf20Sopenharmony_ci ctlr->update_mac = fcoe_update_src_mac; 4168c2ecf20Sopenharmony_ci ctlr->get_src_addr = fcoe_get_src_mac; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci err = fcoe_interface_setup(fcoe, netdev); 4198c2ecf20Sopenharmony_ci if (err) { 4208c2ecf20Sopenharmony_ci fcoe_ctlr_destroy(ctlr); 4218c2ecf20Sopenharmony_ci fcoe_ctlr_device_delete(ctlr_dev); 4228c2ecf20Sopenharmony_ci dev_put(netdev); 4238c2ecf20Sopenharmony_ci fcoe = ERR_PTR(err); 4248c2ecf20Sopenharmony_ci goto out_putmod; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci goto out; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciout_putmod: 4308c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 4318c2ecf20Sopenharmony_ciout: 4328c2ecf20Sopenharmony_ci return fcoe; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * fcoe_interface_remove() - remove FCoE interface from netdev 4378c2ecf20Sopenharmony_ci * @fcoe: The FCoE interface to be cleaned up 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * Caller must be holding the RTNL mutex 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_cistatic void fcoe_interface_remove(struct fcoe_interface *fcoe) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->netdev; 4448c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); 4458c2ecf20Sopenharmony_ci u8 flogi_maddr[ETH_ALEN]; 4468c2ecf20Sopenharmony_ci const struct net_device_ops *ops; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Don't listen for Ethernet packets anymore. 4508c2ecf20Sopenharmony_ci * synchronize_net() ensures that the packet handlers are not running 4518c2ecf20Sopenharmony_ci * on another CPU. dev_remove_pack() would do that, this calls the 4528c2ecf20Sopenharmony_ci * unsyncronized version __dev_remove_pack() to avoid multiple delays. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ci __dev_remove_pack(&fcoe->fcoe_packet_type); 4558c2ecf20Sopenharmony_ci __dev_remove_pack(&fcoe->fip_packet_type); 4568c2ecf20Sopenharmony_ci if (netdev != fcoe->realdev) 4578c2ecf20Sopenharmony_ci __dev_remove_pack(&fcoe->fip_vlan_packet_type); 4588c2ecf20Sopenharmony_ci synchronize_net(); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Delete secondary MAC addresses */ 4618c2ecf20Sopenharmony_ci memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); 4628c2ecf20Sopenharmony_ci dev_uc_del(netdev, flogi_maddr); 4638c2ecf20Sopenharmony_ci if (fip->spma) 4648c2ecf20Sopenharmony_ci dev_uc_del(netdev, fip->ctl_src_addr); 4658c2ecf20Sopenharmony_ci if (fip->mode == FIP_MODE_VN2VN) { 4668c2ecf20Sopenharmony_ci dev_mc_del(netdev, FIP_ALL_VN2VN_MACS); 4678c2ecf20Sopenharmony_ci dev_mc_del(netdev, FIP_ALL_P2P_MACS); 4688c2ecf20Sopenharmony_ci } else 4698c2ecf20Sopenharmony_ci dev_mc_del(netdev, FIP_ALL_ENODE_MACS); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Tell the LLD we are done w/ FCoE */ 4728c2ecf20Sopenharmony_ci ops = netdev->netdev_ops; 4738c2ecf20Sopenharmony_ci if (ops->ndo_fcoe_disable) { 4748c2ecf20Sopenharmony_ci if (ops->ndo_fcoe_disable(netdev)) 4758c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE" 4768c2ecf20Sopenharmony_ci " specific feature for LLD.\n"); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci fcoe->removed = 1; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/** 4838c2ecf20Sopenharmony_ci * fcoe_interface_cleanup() - Clean up a FCoE interface 4848c2ecf20Sopenharmony_ci * @fcoe: The FCoE interface to be cleaned up 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_cistatic void fcoe_interface_cleanup(struct fcoe_interface *fcoe) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->netdev; 4898c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Release the self-reference taken during fcoe_interface_create() */ 4928c2ecf20Sopenharmony_ci /* tear-down the FCoE controller */ 4938c2ecf20Sopenharmony_ci fcoe_ctlr_destroy(fip); 4948c2ecf20Sopenharmony_ci scsi_host_put(fip->lp->host); 4958c2ecf20Sopenharmony_ci dev_put(netdev); 4968c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/** 5008c2ecf20Sopenharmony_ci * fcoe_fip_recv() - Handler for received FIP frames 5018c2ecf20Sopenharmony_ci * @skb: The receive skb 5028c2ecf20Sopenharmony_ci * @netdev: The associated net device 5038c2ecf20Sopenharmony_ci * @ptype: The packet_type structure which was used to register this handler 5048c2ecf20Sopenharmony_ci * @orig_dev: The original net_device the skb was received on. 5058c2ecf20Sopenharmony_ci * (in case dev is a bond) 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * Returns: 0 for success 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, 5108c2ecf20Sopenharmony_ci struct packet_type *ptype, 5118c2ecf20Sopenharmony_ci struct net_device *orig_dev) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 5148c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); 5178c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 5188c2ecf20Sopenharmony_ci fcoe_ctlr_recv(ctlr, skb); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/** 5238c2ecf20Sopenharmony_ci * fcoe_fip_vlan_recv() - Handler for received FIP VLAN discovery frames 5248c2ecf20Sopenharmony_ci * @skb: The receive skb 5258c2ecf20Sopenharmony_ci * @netdev: The associated net device 5268c2ecf20Sopenharmony_ci * @ptype: The packet_type structure which was used to register this handler 5278c2ecf20Sopenharmony_ci * @orig_dev: The original net_device the skb was received on. 5288c2ecf20Sopenharmony_ci * (in case dev is a bond) 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * Returns: 0 for success 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic int fcoe_fip_vlan_recv(struct sk_buff *skb, struct net_device *netdev, 5338c2ecf20Sopenharmony_ci struct packet_type *ptype, 5348c2ecf20Sopenharmony_ci struct net_device *orig_dev) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 5378c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci fcoe = container_of(ptype, struct fcoe_interface, fip_vlan_packet_type); 5408c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 5418c2ecf20Sopenharmony_ci fcoe_ctlr_recv(ctlr, skb); 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/** 5468c2ecf20Sopenharmony_ci * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame 5478c2ecf20Sopenharmony_ci * @port: The FCoE port 5488c2ecf20Sopenharmony_ci * @skb: The FIP/FCoE packet to be sent 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_cistatic void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci if (port->fcoe_pending_queue.qlen) 5538c2ecf20Sopenharmony_ci fcoe_check_wait_queue(port->lport, skb); 5548c2ecf20Sopenharmony_ci else if (fcoe_start_io(skb)) 5558c2ecf20Sopenharmony_ci fcoe_check_wait_queue(port->lport, skb); 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci/** 5598c2ecf20Sopenharmony_ci * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame 5608c2ecf20Sopenharmony_ci * @fip: The FCoE controller 5618c2ecf20Sopenharmony_ci * @skb: The FIP packet to be sent 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_cistatic void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = fcoe_from_ctlr(fip); 5668c2ecf20Sopenharmony_ci struct fip_frame { 5678c2ecf20Sopenharmony_ci struct ethhdr eth; 5688c2ecf20Sopenharmony_ci struct fip_header fip; 5698c2ecf20Sopenharmony_ci } __packed *frame; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * Use default VLAN for FIP VLAN discovery protocol 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci frame = (struct fip_frame *)skb->data; 5758c2ecf20Sopenharmony_ci if (ntohs(frame->eth.h_proto) == ETH_P_FIP && 5768c2ecf20Sopenharmony_ci ntohs(frame->fip.fip_op) == FIP_OP_VLAN && 5778c2ecf20Sopenharmony_ci fcoe->realdev != fcoe->netdev) 5788c2ecf20Sopenharmony_ci skb->dev = fcoe->realdev; 5798c2ecf20Sopenharmony_ci else 5808c2ecf20Sopenharmony_ci skb->dev = fcoe->netdev; 5818c2ecf20Sopenharmony_ci fcoe_port_send(lport_priv(fip->lp), skb); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/** 5858c2ecf20Sopenharmony_ci * fcoe_update_src_mac() - Update the Ethernet MAC filters 5868c2ecf20Sopenharmony_ci * @lport: The local port to update the source MAC on 5878c2ecf20Sopenharmony_ci * @addr: Unicast MAC address to add 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * Remove any previously-set unicast MAC filter. 5908c2ecf20Sopenharmony_ci * Add secondary FCoE MAC address filter for our OUI. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 5958c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(port->data_src_addr)) 5988c2ecf20Sopenharmony_ci dev_uc_del(fcoe->netdev, port->data_src_addr); 5998c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(addr)) 6008c2ecf20Sopenharmony_ci dev_uc_add(fcoe->netdev, addr); 6018c2ecf20Sopenharmony_ci memcpy(port->data_src_addr, addr, ETH_ALEN); 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci/** 6058c2ecf20Sopenharmony_ci * fcoe_get_src_mac() - return the Ethernet source address for an lport 6068c2ecf20Sopenharmony_ci * @lport: libfc lport 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_cistatic u8 *fcoe_get_src_mac(struct fc_lport *lport) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci return port->data_src_addr; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci/** 6168c2ecf20Sopenharmony_ci * fcoe_lport_config() - Set up a local port 6178c2ecf20Sopenharmony_ci * @lport: The local port to be setup 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * Returns: 0 for success 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistatic int fcoe_lport_config(struct fc_lport *lport) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci lport->link_up = 0; 6248c2ecf20Sopenharmony_ci lport->qfull = 0; 6258c2ecf20Sopenharmony_ci lport->max_retry_count = 3; 6268c2ecf20Sopenharmony_ci lport->max_rport_retry_count = 3; 6278c2ecf20Sopenharmony_ci lport->e_d_tov = fcoe_e_d_tov; 6288c2ecf20Sopenharmony_ci lport->r_a_tov = fcoe_r_a_tov; 6298c2ecf20Sopenharmony_ci lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | 6308c2ecf20Sopenharmony_ci FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); 6318c2ecf20Sopenharmony_ci lport->does_npiv = 1; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci fc_lport_init_stats(lport); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* lport fc_lport related configuration */ 6368c2ecf20Sopenharmony_ci fc_lport_config(lport); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* offload related configuration */ 6398c2ecf20Sopenharmony_ci lport->crc_offload = 0; 6408c2ecf20Sopenharmony_ci lport->seq_offload = 0; 6418c2ecf20Sopenharmony_ci lport->lro_enabled = 0; 6428c2ecf20Sopenharmony_ci lport->lro_xid = 0; 6438c2ecf20Sopenharmony_ci lport->lso_max = 0; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* 6498c2ecf20Sopenharmony_ci * fcoe_netdev_features_change - Updates the lport's offload flags based 6508c2ecf20Sopenharmony_ci * on the LLD netdev's FCoE feature flags 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic void fcoe_netdev_features_change(struct fc_lport *lport, 6538c2ecf20Sopenharmony_ci struct net_device *netdev) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci mutex_lock(&lport->lp_mutex); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_SG) 6588c2ecf20Sopenharmony_ci lport->sg_supp = 1; 6598c2ecf20Sopenharmony_ci else 6608c2ecf20Sopenharmony_ci lport->sg_supp = 0; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_FCOE_CRC) { 6638c2ecf20Sopenharmony_ci lport->crc_offload = 1; 6648c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); 6658c2ecf20Sopenharmony_ci } else { 6668c2ecf20Sopenharmony_ci lport->crc_offload = 0; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_FSO) { 6708c2ecf20Sopenharmony_ci lport->seq_offload = 1; 6718c2ecf20Sopenharmony_ci lport->lso_max = netdev->gso_max_size; 6728c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", 6738c2ecf20Sopenharmony_ci lport->lso_max); 6748c2ecf20Sopenharmony_ci } else { 6758c2ecf20Sopenharmony_ci lport->seq_offload = 0; 6768c2ecf20Sopenharmony_ci lport->lso_max = 0; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (netdev->fcoe_ddp_xid) { 6808c2ecf20Sopenharmony_ci lport->lro_enabled = 1; 6818c2ecf20Sopenharmony_ci lport->lro_xid = netdev->fcoe_ddp_xid; 6828c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", 6838c2ecf20Sopenharmony_ci lport->lro_xid); 6848c2ecf20Sopenharmony_ci } else { 6858c2ecf20Sopenharmony_ci lport->lro_enabled = 0; 6868c2ecf20Sopenharmony_ci lport->lro_xid = 0; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mutex_unlock(&lport->lp_mutex); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/** 6938c2ecf20Sopenharmony_ci * fcoe_netdev_config() - Set up net devive for SW FCoE 6948c2ecf20Sopenharmony_ci * @lport: The local port that is associated with the net device 6958c2ecf20Sopenharmony_ci * @netdev: The associated net device 6968c2ecf20Sopenharmony_ci * 6978c2ecf20Sopenharmony_ci * Must be called after fcoe_lport_config() as it will use local port mutex 6988c2ecf20Sopenharmony_ci * 6998c2ecf20Sopenharmony_ci * Returns: 0 for success 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_cistatic int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci u32 mfs; 7048c2ecf20Sopenharmony_ci u64 wwnn, wwpn; 7058c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 7068c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 7078c2ecf20Sopenharmony_ci struct fcoe_port *port; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Setup lport private data to point to fcoe softc */ 7108c2ecf20Sopenharmony_ci port = lport_priv(lport); 7118c2ecf20Sopenharmony_ci fcoe = port->priv; 7128c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Figure out the VLAN ID, if any */ 7158c2ecf20Sopenharmony_ci if (is_vlan_dev(netdev)) 7168c2ecf20Sopenharmony_ci lport->vlan = vlan_dev_vlan_id(netdev); 7178c2ecf20Sopenharmony_ci else 7188c2ecf20Sopenharmony_ci lport->vlan = 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 7218c2ecf20Sopenharmony_ci * Determine max frame size based on underlying device and optional 7228c2ecf20Sopenharmony_ci * user-configured limit. If the MFS is too low, fcoe_link_ok() 7238c2ecf20Sopenharmony_ci * will return 0, so do this first. 7248c2ecf20Sopenharmony_ci */ 7258c2ecf20Sopenharmony_ci mfs = netdev->mtu; 7268c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_FCOE_MTU) { 7278c2ecf20Sopenharmony_ci mfs = FCOE_MTU; 7288c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof)); 7318c2ecf20Sopenharmony_ci if (fc_set_mfs(lport, mfs)) 7328c2ecf20Sopenharmony_ci return -EINVAL; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* offload features support */ 7358c2ecf20Sopenharmony_ci fcoe_netdev_features_change(lport, netdev); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci skb_queue_head_init(&port->fcoe_pending_queue); 7388c2ecf20Sopenharmony_ci port->fcoe_pending_queue_active = 0; 7398c2ecf20Sopenharmony_ci timer_setup(&port->timer, fcoe_queue_timer, 0); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci fcoe_link_speed_update(lport); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (!lport->vport) { 7448c2ecf20Sopenharmony_ci if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) 7458c2ecf20Sopenharmony_ci wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 1, 0); 7468c2ecf20Sopenharmony_ci fc_set_wwnn(lport, wwnn); 7478c2ecf20Sopenharmony_ci if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN)) 7488c2ecf20Sopenharmony_ci wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr, 7498c2ecf20Sopenharmony_ci 2, 0); 7508c2ecf20Sopenharmony_ci fc_set_wwpn(lport, wwpn); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci/** 7578c2ecf20Sopenharmony_ci * fcoe_shost_config() - Set up the SCSI host associated with a local port 7588c2ecf20Sopenharmony_ci * @lport: The local port 7598c2ecf20Sopenharmony_ci * @dev: The device associated with the SCSI host 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * Must be called after fcoe_lport_config() and fcoe_netdev_config() 7628c2ecf20Sopenharmony_ci * 7638c2ecf20Sopenharmony_ci * Returns: 0 for success 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_cistatic int fcoe_shost_config(struct fc_lport *lport, struct device *dev) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci int rc = 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* lport scsi host config */ 7708c2ecf20Sopenharmony_ci lport->host->max_lun = FCOE_MAX_LUN; 7718c2ecf20Sopenharmony_ci lport->host->max_id = FCOE_MAX_FCP_TARGET; 7728c2ecf20Sopenharmony_ci lport->host->max_channel = 0; 7738c2ecf20Sopenharmony_ci lport->host->max_cmd_len = FCOE_MAX_CMD_LEN; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (lport->vport) 7768c2ecf20Sopenharmony_ci lport->host->transportt = fcoe_vport_scsi_transport; 7778c2ecf20Sopenharmony_ci else 7788c2ecf20Sopenharmony_ci lport->host->transportt = fcoe_nport_scsi_transport; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* add the new host to the SCSI-ml */ 7818c2ecf20Sopenharmony_ci rc = scsi_add_host(lport->host, dev); 7828c2ecf20Sopenharmony_ci if (rc) { 7838c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: " 7848c2ecf20Sopenharmony_ci "error on scsi_add_host\n"); 7858c2ecf20Sopenharmony_ci return rc; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!lport->vport) 7898c2ecf20Sopenharmony_ci fc_host_max_npiv_vports(lport->host) = USHRT_MAX; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, 7928c2ecf20Sopenharmony_ci "%s v%s over %s", FCOE_NAME, FCOE_VERSION, 7938c2ecf20Sopenharmony_ci fcoe_netdev(lport)->name); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci return 0; 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci/** 8008c2ecf20Sopenharmony_ci * fcoe_fdmi_info() - Get FDMI related info from net devive for SW FCoE 8018c2ecf20Sopenharmony_ci * @lport: The local port that is associated with the net device 8028c2ecf20Sopenharmony_ci * @netdev: The associated net device 8038c2ecf20Sopenharmony_ci * 8048c2ecf20Sopenharmony_ci * Must be called after fcoe_shost_config() as it will use local port mutex 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_cistatic void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 8108c2ecf20Sopenharmony_ci struct fcoe_port *port; 8118c2ecf20Sopenharmony_ci struct net_device *realdev; 8128c2ecf20Sopenharmony_ci int rc; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci port = lport_priv(lport); 8158c2ecf20Sopenharmony_ci fcoe = port->priv; 8168c2ecf20Sopenharmony_ci realdev = fcoe->realdev; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* No FDMI state m/c for NPIV ports */ 8198c2ecf20Sopenharmony_ci if (lport->vport) 8208c2ecf20Sopenharmony_ci return; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) { 8238c2ecf20Sopenharmony_ci struct netdev_fcoe_hbainfo *fdmi; 8248c2ecf20Sopenharmony_ci fdmi = kzalloc(sizeof(*fdmi), GFP_KERNEL); 8258c2ecf20Sopenharmony_ci if (!fdmi) 8268c2ecf20Sopenharmony_ci return; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev, 8298c2ecf20Sopenharmony_ci fdmi); 8308c2ecf20Sopenharmony_ci if (rc) { 8318c2ecf20Sopenharmony_ci printk(KERN_INFO "fcoe: Failed to retrieve FDMI " 8328c2ecf20Sopenharmony_ci "information from netdev.\n"); 8338c2ecf20Sopenharmony_ci return; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci snprintf(fc_host_serial_number(lport->host), 8378c2ecf20Sopenharmony_ci FC_SERIAL_NUMBER_SIZE, 8388c2ecf20Sopenharmony_ci "%s", 8398c2ecf20Sopenharmony_ci fdmi->serial_number); 8408c2ecf20Sopenharmony_ci snprintf(fc_host_manufacturer(lport->host), 8418c2ecf20Sopenharmony_ci FC_SERIAL_NUMBER_SIZE, 8428c2ecf20Sopenharmony_ci "%s", 8438c2ecf20Sopenharmony_ci fdmi->manufacturer); 8448c2ecf20Sopenharmony_ci snprintf(fc_host_model(lport->host), 8458c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, 8468c2ecf20Sopenharmony_ci "%s", 8478c2ecf20Sopenharmony_ci fdmi->model); 8488c2ecf20Sopenharmony_ci snprintf(fc_host_model_description(lport->host), 8498c2ecf20Sopenharmony_ci FC_SYMBOLIC_NAME_SIZE, 8508c2ecf20Sopenharmony_ci "%s", 8518c2ecf20Sopenharmony_ci fdmi->model_description); 8528c2ecf20Sopenharmony_ci snprintf(fc_host_hardware_version(lport->host), 8538c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, 8548c2ecf20Sopenharmony_ci "%s", 8558c2ecf20Sopenharmony_ci fdmi->hardware_version); 8568c2ecf20Sopenharmony_ci snprintf(fc_host_driver_version(lport->host), 8578c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, 8588c2ecf20Sopenharmony_ci "%s", 8598c2ecf20Sopenharmony_ci fdmi->driver_version); 8608c2ecf20Sopenharmony_ci snprintf(fc_host_optionrom_version(lport->host), 8618c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, 8628c2ecf20Sopenharmony_ci "%s", 8638c2ecf20Sopenharmony_ci fdmi->optionrom_version); 8648c2ecf20Sopenharmony_ci snprintf(fc_host_firmware_version(lport->host), 8658c2ecf20Sopenharmony_ci FC_VERSION_STRING_SIZE, 8668c2ecf20Sopenharmony_ci "%s", 8678c2ecf20Sopenharmony_ci fdmi->firmware_version); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Enable FDMI lport states */ 8708c2ecf20Sopenharmony_ci lport->fdmi_enabled = 1; 8718c2ecf20Sopenharmony_ci kfree(fdmi); 8728c2ecf20Sopenharmony_ci } else { 8738c2ecf20Sopenharmony_ci lport->fdmi_enabled = 0; 8748c2ecf20Sopenharmony_ci printk(KERN_INFO "fcoe: No FDMI support.\n"); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci/** 8798c2ecf20Sopenharmony_ci * fcoe_oem_match() - The match routine for the offloaded exchange manager 8808c2ecf20Sopenharmony_ci * @fp: The I/O frame 8818c2ecf20Sopenharmony_ci * 8828c2ecf20Sopenharmony_ci * This routine will be associated with an exchange manager (EM). When 8838c2ecf20Sopenharmony_ci * the libfc exchange handling code is looking for an EM to use it will 8848c2ecf20Sopenharmony_ci * call this routine and pass it the frame that it wishes to send. This 8858c2ecf20Sopenharmony_ci * routine will return True if the associated EM is to be used and False 8868c2ecf20Sopenharmony_ci * if the echange code should continue looking for an EM. 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * The offload EM that this routine is associated with will handle any 8898c2ecf20Sopenharmony_ci * packets that are for SCSI read requests. 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * This has been enhanced to work when FCoE stack is operating in target 8928c2ecf20Sopenharmony_ci * mode. 8938c2ecf20Sopenharmony_ci * 8948c2ecf20Sopenharmony_ci * Returns: True for read types I/O, otherwise returns false. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_cistatic bool fcoe_oem_match(struct fc_frame *fp) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci struct fc_frame_header *fh = fc_frame_header_get(fp); 8998c2ecf20Sopenharmony_ci struct fcp_cmnd *fcp; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (fc_fcp_is_read(fr_fsp(fp)) && 9028c2ecf20Sopenharmony_ci (fr_fsp(fp)->data_len > fcoe_ddp_min)) 9038c2ecf20Sopenharmony_ci return true; 9048c2ecf20Sopenharmony_ci else if ((fr_fsp(fp) == NULL) && 9058c2ecf20Sopenharmony_ci (fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) && 9068c2ecf20Sopenharmony_ci (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)) { 9078c2ecf20Sopenharmony_ci fcp = fc_frame_payload_get(fp, sizeof(*fcp)); 9088c2ecf20Sopenharmony_ci if ((fcp->fc_flags & FCP_CFL_WRDATA) && 9098c2ecf20Sopenharmony_ci (ntohl(fcp->fc_dl) > fcoe_ddp_min)) 9108c2ecf20Sopenharmony_ci return true; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci return false; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/** 9168c2ecf20Sopenharmony_ci * fcoe_em_config() - Allocate and configure an exchange manager 9178c2ecf20Sopenharmony_ci * @lport: The local port that the new EM will be associated with 9188c2ecf20Sopenharmony_ci * 9198c2ecf20Sopenharmony_ci * Returns: 0 on success 9208c2ecf20Sopenharmony_ci */ 9218c2ecf20Sopenharmony_cistatic inline int fcoe_em_config(struct fc_lport *lport) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 9248c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 9258c2ecf20Sopenharmony_ci struct fcoe_interface *oldfcoe = NULL; 9268c2ecf20Sopenharmony_ci struct net_device *old_real_dev, *cur_real_dev; 9278c2ecf20Sopenharmony_ci u16 min_xid = FCOE_MIN_XID; 9288c2ecf20Sopenharmony_ci u16 max_xid = FCOE_MAX_XID; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* 9318c2ecf20Sopenharmony_ci * Check if need to allocate an em instance for 9328c2ecf20Sopenharmony_ci * offload exchange ids to be shared across all VN_PORTs/lport. 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci if (!lport->lro_enabled || !lport->lro_xid || 9358c2ecf20Sopenharmony_ci (lport->lro_xid >= max_xid)) { 9368c2ecf20Sopenharmony_ci lport->lro_xid = 0; 9378c2ecf20Sopenharmony_ci goto skip_oem; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * Reuse existing offload em instance in case 9428c2ecf20Sopenharmony_ci * it is already allocated on real eth device 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci if (is_vlan_dev(fcoe->netdev)) 9458c2ecf20Sopenharmony_ci cur_real_dev = vlan_dev_real_dev(fcoe->netdev); 9468c2ecf20Sopenharmony_ci else 9478c2ecf20Sopenharmony_ci cur_real_dev = fcoe->netdev; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci list_for_each_entry(oldfcoe, &fcoe_hostlist, list) { 9508c2ecf20Sopenharmony_ci if (is_vlan_dev(oldfcoe->netdev)) 9518c2ecf20Sopenharmony_ci old_real_dev = vlan_dev_real_dev(oldfcoe->netdev); 9528c2ecf20Sopenharmony_ci else 9538c2ecf20Sopenharmony_ci old_real_dev = oldfcoe->netdev; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (cur_real_dev == old_real_dev) { 9568c2ecf20Sopenharmony_ci fcoe->oem = oldfcoe->oem; 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci if (fcoe->oem) { 9628c2ecf20Sopenharmony_ci if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) { 9638c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe_em_config: failed to add " 9648c2ecf20Sopenharmony_ci "offload em:%p on interface:%s\n", 9658c2ecf20Sopenharmony_ci fcoe->oem, fcoe->netdev->name); 9668c2ecf20Sopenharmony_ci return -ENOMEM; 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci } else { 9698c2ecf20Sopenharmony_ci fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3, 9708c2ecf20Sopenharmony_ci FCOE_MIN_XID, lport->lro_xid, 9718c2ecf20Sopenharmony_ci fcoe_oem_match); 9728c2ecf20Sopenharmony_ci if (!fcoe->oem) { 9738c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe_em_config: failed to allocate " 9748c2ecf20Sopenharmony_ci "em for offload exches on interface:%s\n", 9758c2ecf20Sopenharmony_ci fcoe->netdev->name); 9768c2ecf20Sopenharmony_ci return -ENOMEM; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * Exclude offload EM xid range from next EM xid range. 9828c2ecf20Sopenharmony_ci */ 9838c2ecf20Sopenharmony_ci min_xid += lport->lro_xid + 1; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ciskip_oem: 9868c2ecf20Sopenharmony_ci if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) { 9878c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe_em_config: failed to " 9888c2ecf20Sopenharmony_ci "allocate em on interface %s\n", fcoe->netdev->name); 9898c2ecf20Sopenharmony_ci return -ENOMEM; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci/** 9968c2ecf20Sopenharmony_ci * fcoe_if_destroy() - Tear down a SW FCoE instance 9978c2ecf20Sopenharmony_ci * @lport: The local port to be destroyed 9988c2ecf20Sopenharmony_ci * 9998c2ecf20Sopenharmony_ci * Locking: Must be called with the RTNL mutex held. 10008c2ecf20Sopenharmony_ci * 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_cistatic void fcoe_if_destroy(struct fc_lport *lport) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 10058c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 10068c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->netdev; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Logout of the fabric */ 10118c2ecf20Sopenharmony_ci fc_fabric_logoff(lport); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* Cleanup the fc_lport */ 10148c2ecf20Sopenharmony_ci fc_lport_destroy(lport); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Stop the transmit retry timer */ 10178c2ecf20Sopenharmony_ci del_timer_sync(&port->timer); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Free existing transmit skbs */ 10208c2ecf20Sopenharmony_ci fcoe_clean_pending_queue(lport); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(port->data_src_addr)) 10238c2ecf20Sopenharmony_ci dev_uc_del(netdev, port->data_src_addr); 10248c2ecf20Sopenharmony_ci if (lport->vport) 10258c2ecf20Sopenharmony_ci synchronize_net(); 10268c2ecf20Sopenharmony_ci else 10278c2ecf20Sopenharmony_ci fcoe_interface_remove(fcoe); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* Free queued packets for the per-CPU receive threads */ 10308c2ecf20Sopenharmony_ci fcoe_percpu_clean(lport); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* Detach from the scsi-ml */ 10338c2ecf20Sopenharmony_ci fc_remove_host(lport->host); 10348c2ecf20Sopenharmony_ci scsi_remove_host(lport->host); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Destroy lport scsi_priv */ 10378c2ecf20Sopenharmony_ci fc_fcp_destroy(lport); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* There are no more rports or I/O, free the EM */ 10408c2ecf20Sopenharmony_ci fc_exch_mgr_free(lport); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* Free memory used by statistical counters */ 10438c2ecf20Sopenharmony_ci fc_lport_free_stats(lport); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * Release the Scsi_Host for vport but hold on to 10478c2ecf20Sopenharmony_ci * master lport until it fcoe interface fully cleaned-up. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (lport->vport) 10508c2ecf20Sopenharmony_ci scsi_host_put(lport->host); 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci/** 10548c2ecf20Sopenharmony_ci * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device 10558c2ecf20Sopenharmony_ci * @lport: The local port to setup DDP for 10568c2ecf20Sopenharmony_ci * @xid: The exchange ID for this DDP transfer 10578c2ecf20Sopenharmony_ci * @sgl: The scatterlist describing this transfer 10588c2ecf20Sopenharmony_ci * @sgc: The number of sg items 10598c2ecf20Sopenharmony_ci * 10608c2ecf20Sopenharmony_ci * Returns: 0 if the DDP context was not configured 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_cistatic int fcoe_ddp_setup(struct fc_lport *lport, u16 xid, 10638c2ecf20Sopenharmony_ci struct scatterlist *sgl, unsigned int sgc) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe_netdev(lport); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (netdev->netdev_ops->ndo_fcoe_ddp_setup) 10688c2ecf20Sopenharmony_ci return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev, 10698c2ecf20Sopenharmony_ci xid, sgl, 10708c2ecf20Sopenharmony_ci sgc); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return 0; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci/** 10768c2ecf20Sopenharmony_ci * fcoe_ddp_target() - Call a LLD's ddp_target through the net device 10778c2ecf20Sopenharmony_ci * @lport: The local port to setup DDP for 10788c2ecf20Sopenharmony_ci * @xid: The exchange ID for this DDP transfer 10798c2ecf20Sopenharmony_ci * @sgl: The scatterlist describing this transfer 10808c2ecf20Sopenharmony_ci * @sgc: The number of sg items 10818c2ecf20Sopenharmony_ci * 10828c2ecf20Sopenharmony_ci * Returns: 0 if the DDP context was not configured 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_cistatic int fcoe_ddp_target(struct fc_lport *lport, u16 xid, 10858c2ecf20Sopenharmony_ci struct scatterlist *sgl, unsigned int sgc) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe_netdev(lport); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (netdev->netdev_ops->ndo_fcoe_ddp_target) 10908c2ecf20Sopenharmony_ci return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid, 10918c2ecf20Sopenharmony_ci sgl, sgc); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci return 0; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/** 10988c2ecf20Sopenharmony_ci * fcoe_ddp_done() - Call a LLD's ddp_done through the net device 10998c2ecf20Sopenharmony_ci * @lport: The local port to complete DDP on 11008c2ecf20Sopenharmony_ci * @xid: The exchange ID for this DDP transfer 11018c2ecf20Sopenharmony_ci * 11028c2ecf20Sopenharmony_ci * Returns: the length of data that have been completed by DDP 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic int fcoe_ddp_done(struct fc_lport *lport, u16 xid) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe_netdev(lport); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (netdev->netdev_ops->ndo_fcoe_ddp_done) 11098c2ecf20Sopenharmony_ci return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid); 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci/** 11148c2ecf20Sopenharmony_ci * fcoe_if_create() - Create a FCoE instance on an interface 11158c2ecf20Sopenharmony_ci * @fcoe: The FCoE interface to create a local port on 11168c2ecf20Sopenharmony_ci * @parent: The device pointer to be the parent in sysfs for the SCSI host 11178c2ecf20Sopenharmony_ci * @npiv: Indicates if the port is a vport or not 11188c2ecf20Sopenharmony_ci * 11198c2ecf20Sopenharmony_ci * Creates a fc_lport instance and a Scsi_Host instance and configure them. 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * Returns: The allocated fc_lport or an error pointer 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_cistatic struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, 11248c2ecf20Sopenharmony_ci struct device *parent, int npiv) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); 11278c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->netdev; 11288c2ecf20Sopenharmony_ci struct fc_lport *lport, *n_port; 11298c2ecf20Sopenharmony_ci struct fcoe_port *port; 11308c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 11318c2ecf20Sopenharmony_ci int rc; 11328c2ecf20Sopenharmony_ci /* 11338c2ecf20Sopenharmony_ci * parent is only a vport if npiv is 1, 11348c2ecf20Sopenharmony_ci * but we'll only use vport in that case so go ahead and set it 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci struct fc_vport *vport = dev_to_vport(parent); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Create Interface\n"); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (!npiv) 11418c2ecf20Sopenharmony_ci lport = libfc_host_alloc(&fcoe_shost_template, sizeof(*port)); 11428c2ecf20Sopenharmony_ci else 11438c2ecf20Sopenharmony_ci lport = libfc_vport_create(vport, sizeof(*port)); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (!lport) { 11468c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); 11478c2ecf20Sopenharmony_ci rc = -ENOMEM; 11488c2ecf20Sopenharmony_ci goto out; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci port = lport_priv(lport); 11518c2ecf20Sopenharmony_ci port->lport = lport; 11528c2ecf20Sopenharmony_ci port->priv = fcoe; 11538c2ecf20Sopenharmony_ci port->get_netdev = fcoe_netdev; 11548c2ecf20Sopenharmony_ci port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH; 11558c2ecf20Sopenharmony_ci port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; 11568c2ecf20Sopenharmony_ci INIT_WORK(&port->destroy_work, fcoe_destroy_work); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * Need to add the lport to the hostlist 11608c2ecf20Sopenharmony_ci * so we catch NETDEV_CHANGE events. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci fcoe_hostlist_add(lport); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* configure a fc_lport including the exchange manager */ 11658c2ecf20Sopenharmony_ci rc = fcoe_lport_config(lport); 11668c2ecf20Sopenharmony_ci if (rc) { 11678c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " 11688c2ecf20Sopenharmony_ci "interface\n"); 11698c2ecf20Sopenharmony_ci goto out_host_put; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci if (npiv) { 11738c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Setting vport names, " 11748c2ecf20Sopenharmony_ci "%16.16llx %16.16llx\n", 11758c2ecf20Sopenharmony_ci vport->node_name, vport->port_name); 11768c2ecf20Sopenharmony_ci fc_set_wwnn(lport, vport->node_name); 11778c2ecf20Sopenharmony_ci fc_set_wwpn(lport, vport->port_name); 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* configure lport network properties */ 11818c2ecf20Sopenharmony_ci rc = fcoe_netdev_config(lport, netdev); 11828c2ecf20Sopenharmony_ci if (rc) { 11838c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " 11848c2ecf20Sopenharmony_ci "interface\n"); 11858c2ecf20Sopenharmony_ci goto out_lp_destroy; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci /* configure lport scsi host properties */ 11898c2ecf20Sopenharmony_ci rc = fcoe_shost_config(lport, parent); 11908c2ecf20Sopenharmony_ci if (rc) { 11918c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " 11928c2ecf20Sopenharmony_ci "interface\n"); 11938c2ecf20Sopenharmony_ci goto out_lp_destroy; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* Initialize the library */ 11978c2ecf20Sopenharmony_ci rc = fcoe_libfc_config(lport, ctlr, &fcoe_libfc_fcn_templ, 1); 11988c2ecf20Sopenharmony_ci if (rc) { 11998c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " 12008c2ecf20Sopenharmony_ci "interface\n"); 12018c2ecf20Sopenharmony_ci goto out_lp_destroy; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* Initialized FDMI information */ 12058c2ecf20Sopenharmony_ci fcoe_fdmi_info(lport, netdev); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci /* 12088c2ecf20Sopenharmony_ci * fcoe_em_alloc() and fcoe_hostlist_add() both 12098c2ecf20Sopenharmony_ci * need to be atomic with respect to other changes to the 12108c2ecf20Sopenharmony_ci * hostlist since fcoe_em_alloc() looks for an existing EM 12118c2ecf20Sopenharmony_ci * instance on host list updated by fcoe_hostlist_add(). 12128c2ecf20Sopenharmony_ci * 12138c2ecf20Sopenharmony_ci * This is currently handled through the fcoe_config_mutex 12148c2ecf20Sopenharmony_ci * begin held. 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_ci if (!npiv) 12178c2ecf20Sopenharmony_ci /* lport exch manager allocation */ 12188c2ecf20Sopenharmony_ci rc = fcoe_em_config(lport); 12198c2ecf20Sopenharmony_ci else { 12208c2ecf20Sopenharmony_ci shost = vport_to_shost(vport); 12218c2ecf20Sopenharmony_ci n_port = shost_priv(shost); 12228c2ecf20Sopenharmony_ci rc = fc_exch_mgr_list_clone(n_port, lport); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (rc) { 12268c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Could not configure the EM\n"); 12278c2ecf20Sopenharmony_ci goto out_lp_destroy; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci return lport; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ciout_lp_destroy: 12338c2ecf20Sopenharmony_ci fc_exch_mgr_free(lport); 12348c2ecf20Sopenharmony_ciout_host_put: 12358c2ecf20Sopenharmony_ci fcoe_hostlist_del(lport); 12368c2ecf20Sopenharmony_ci scsi_host_put(lport->host); 12378c2ecf20Sopenharmony_ciout: 12388c2ecf20Sopenharmony_ci return ERR_PTR(rc); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci/** 12428c2ecf20Sopenharmony_ci * fcoe_if_init() - Initialization routine for fcoe.ko 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * Attaches the SW FCoE transport to the FC transport 12458c2ecf20Sopenharmony_ci * 12468c2ecf20Sopenharmony_ci * Returns: 0 on success 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_cistatic int __init fcoe_if_init(void) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci /* attach to scsi transport */ 12518c2ecf20Sopenharmony_ci fcoe_nport_scsi_transport = 12528c2ecf20Sopenharmony_ci fc_attach_transport(&fcoe_nport_fc_functions); 12538c2ecf20Sopenharmony_ci if (!fcoe_nport_scsi_transport) 12548c2ecf20Sopenharmony_ci goto err; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci fcoe_vport_scsi_transport = 12578c2ecf20Sopenharmony_ci fc_attach_transport(&fcoe_vport_fc_functions); 12588c2ecf20Sopenharmony_ci if (!fcoe_vport_scsi_transport) 12598c2ecf20Sopenharmony_ci goto err_vport; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci return 0; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cierr_vport: 12648c2ecf20Sopenharmony_ci fc_release_transport(fcoe_nport_scsi_transport); 12658c2ecf20Sopenharmony_cierr: 12668c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n"); 12678c2ecf20Sopenharmony_ci return -ENODEV; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/** 12718c2ecf20Sopenharmony_ci * fcoe_if_exit() - Tear down fcoe.ko 12728c2ecf20Sopenharmony_ci * 12738c2ecf20Sopenharmony_ci * Detaches the SW FCoE transport from the FC transport 12748c2ecf20Sopenharmony_ci * 12758c2ecf20Sopenharmony_ci * Returns: 0 on success 12768c2ecf20Sopenharmony_ci */ 12778c2ecf20Sopenharmony_cistatic int __exit fcoe_if_exit(void) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci fc_release_transport(fcoe_nport_scsi_transport); 12808c2ecf20Sopenharmony_ci fc_release_transport(fcoe_vport_scsi_transport); 12818c2ecf20Sopenharmony_ci fcoe_nport_scsi_transport = NULL; 12828c2ecf20Sopenharmony_ci fcoe_vport_scsi_transport = NULL; 12838c2ecf20Sopenharmony_ci return 0; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic void fcoe_thread_cleanup_local(unsigned int cpu) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci struct page *crc_eof; 12898c2ecf20Sopenharmony_ci struct fcoe_percpu_s *p; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci p = per_cpu_ptr(&fcoe_percpu, cpu); 12928c2ecf20Sopenharmony_ci spin_lock_bh(&p->fcoe_rx_list.lock); 12938c2ecf20Sopenharmony_ci crc_eof = p->crc_eof_page; 12948c2ecf20Sopenharmony_ci p->crc_eof_page = NULL; 12958c2ecf20Sopenharmony_ci p->crc_eof_offset = 0; 12968c2ecf20Sopenharmony_ci spin_unlock_bh(&p->fcoe_rx_list.lock); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (crc_eof) 12998c2ecf20Sopenharmony_ci put_page(crc_eof); 13008c2ecf20Sopenharmony_ci flush_work(&p->work); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci/** 13048c2ecf20Sopenharmony_ci * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming 13058c2ecf20Sopenharmony_ci * command. 13068c2ecf20Sopenharmony_ci * 13078c2ecf20Sopenharmony_ci * This routine selects next CPU based on cpumask to distribute 13088c2ecf20Sopenharmony_ci * incoming requests in round robin. 13098c2ecf20Sopenharmony_ci * 13108c2ecf20Sopenharmony_ci * Returns: int CPU number 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_cistatic inline unsigned int fcoe_select_cpu(void) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci static unsigned int selected_cpu; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci selected_cpu = cpumask_next(selected_cpu, cpu_online_mask); 13178c2ecf20Sopenharmony_ci if (selected_cpu >= nr_cpu_ids) 13188c2ecf20Sopenharmony_ci selected_cpu = cpumask_first(cpu_online_mask); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci return selected_cpu; 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci/** 13248c2ecf20Sopenharmony_ci * fcoe_rcv() - Receive packets from a net device 13258c2ecf20Sopenharmony_ci * @skb: The received packet 13268c2ecf20Sopenharmony_ci * @netdev: The net device that the packet was received on 13278c2ecf20Sopenharmony_ci * @ptype: The packet type context 13288c2ecf20Sopenharmony_ci * @olddev: The last device net device 13298c2ecf20Sopenharmony_ci * 13308c2ecf20Sopenharmony_ci * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a 13318c2ecf20Sopenharmony_ci * FC frame and passes the frame to libfc. 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * Returns: 0 for success 13348c2ecf20Sopenharmony_ci */ 13358c2ecf20Sopenharmony_cistatic int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, 13368c2ecf20Sopenharmony_ci struct packet_type *ptype, struct net_device *olddev) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct fc_lport *lport; 13398c2ecf20Sopenharmony_ci struct fcoe_rcv_info *fr; 13408c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 13418c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 13428c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 13438c2ecf20Sopenharmony_ci struct fcoe_percpu_s *fps; 13448c2ecf20Sopenharmony_ci struct ethhdr *eh; 13458c2ecf20Sopenharmony_ci unsigned int cpu; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); 13488c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 13498c2ecf20Sopenharmony_ci lport = ctlr->lp; 13508c2ecf20Sopenharmony_ci if (unlikely(!lport)) { 13518c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Cannot find hba structure\n"); 13528c2ecf20Sopenharmony_ci goto err2; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci if (!lport->link_up) 13558c2ecf20Sopenharmony_ci goto err2; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, 13588c2ecf20Sopenharmony_ci "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n", 13598c2ecf20Sopenharmony_ci skb->len, skb->data_len, skb->head, skb->data, 13608c2ecf20Sopenharmony_ci skb_tail_pointer(skb), skb_end_pointer(skb), 13618c2ecf20Sopenharmony_ci skb->csum, skb->dev ? skb->dev->name : "<NULL>"); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (skb == NULL) 13678c2ecf20Sopenharmony_ci return NET_RX_DROP; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci eh = eth_hdr(skb); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (is_fip_mode(ctlr) && 13728c2ecf20Sopenharmony_ci !ether_addr_equal(eh->h_source, ctlr->dest_addr)) { 13738c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n", 13748c2ecf20Sopenharmony_ci eh->h_source); 13758c2ecf20Sopenharmony_ci goto err; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* 13798c2ecf20Sopenharmony_ci * Check for minimum frame length, and make sure required FCoE 13808c2ecf20Sopenharmony_ci * and FC headers are pulled into the linear data area. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_ci if (unlikely((skb->len < FCOE_MIN_FRAME) || 13838c2ecf20Sopenharmony_ci !pskb_may_pull(skb, FCOE_HEADER_LEN))) 13848c2ecf20Sopenharmony_ci goto err; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); 13878c2ecf20Sopenharmony_ci fh = (struct fc_frame_header *) skb_transport_header(skb); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (ntoh24(&eh->h_dest[3]) != ntoh24(fh->fh_d_id)) { 13908c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "FC frame d_id mismatch with MAC:%pM\n", 13918c2ecf20Sopenharmony_ci eh->h_dest); 13928c2ecf20Sopenharmony_ci goto err; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci fr = fcoe_dev_from_skb(skb); 13968c2ecf20Sopenharmony_ci fr->fr_dev = lport; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * In case the incoming frame's exchange is originated from 14008c2ecf20Sopenharmony_ci * the initiator, then received frame's exchange id is ANDed 14018c2ecf20Sopenharmony_ci * with fc_cpu_mask bits to get the same cpu on which exchange 14028c2ecf20Sopenharmony_ci * was originated, otherwise select cpu using rx exchange id 14038c2ecf20Sopenharmony_ci * or fcoe_select_cpu(). 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_ci if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) 14068c2ecf20Sopenharmony_ci cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; 14078c2ecf20Sopenharmony_ci else { 14088c2ecf20Sopenharmony_ci if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN) 14098c2ecf20Sopenharmony_ci cpu = fcoe_select_cpu(); 14108c2ecf20Sopenharmony_ci else 14118c2ecf20Sopenharmony_ci cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (cpu >= nr_cpu_ids) 14158c2ecf20Sopenharmony_ci goto err; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci fps = &per_cpu(fcoe_percpu, cpu); 14188c2ecf20Sopenharmony_ci spin_lock(&fps->fcoe_rx_list.lock); 14198c2ecf20Sopenharmony_ci /* 14208c2ecf20Sopenharmony_ci * We now have a valid CPU that we're targeting for 14218c2ecf20Sopenharmony_ci * this skb. We also have this receive thread locked, 14228c2ecf20Sopenharmony_ci * so we're free to queue skbs into it's queue. 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci /* 14268c2ecf20Sopenharmony_ci * Note: We used to have a set of conditions under which we would 14278c2ecf20Sopenharmony_ci * call fcoe_recv_frame directly, rather than queuing to the rx list 14288c2ecf20Sopenharmony_ci * as it could save a few cycles, but doing so is prohibited, as 14298c2ecf20Sopenharmony_ci * fcoe_recv_frame has several paths that may sleep, which is forbidden 14308c2ecf20Sopenharmony_ci * in softirq context. 14318c2ecf20Sopenharmony_ci */ 14328c2ecf20Sopenharmony_ci __skb_queue_tail(&fps->fcoe_rx_list, skb); 14338c2ecf20Sopenharmony_ci schedule_work_on(cpu, &fps->work); 14348c2ecf20Sopenharmony_ci spin_unlock(&fps->fcoe_rx_list.lock); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci return NET_RX_SUCCESS; 14378c2ecf20Sopenharmony_cierr: 14388c2ecf20Sopenharmony_ci per_cpu_ptr(lport->stats, get_cpu())->ErrorFrames++; 14398c2ecf20Sopenharmony_ci put_cpu(); 14408c2ecf20Sopenharmony_cierr2: 14418c2ecf20Sopenharmony_ci kfree_skb(skb); 14428c2ecf20Sopenharmony_ci return NET_RX_DROP; 14438c2ecf20Sopenharmony_ci} 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci/** 14468c2ecf20Sopenharmony_ci * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC 14478c2ecf20Sopenharmony_ci * @skb: The packet to be transmitted 14488c2ecf20Sopenharmony_ci * @tlen: The total length of the trailer 14498c2ecf20Sopenharmony_ci * 14508c2ecf20Sopenharmony_ci * Returns: 0 for success 14518c2ecf20Sopenharmony_ci */ 14528c2ecf20Sopenharmony_cistatic int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci struct fcoe_percpu_s *fps; 14558c2ecf20Sopenharmony_ci int rc; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci fps = &get_cpu_var(fcoe_percpu); 14588c2ecf20Sopenharmony_ci rc = fcoe_get_paged_crc_eof(skb, tlen, fps); 14598c2ecf20Sopenharmony_ci put_cpu_var(fcoe_percpu); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci return rc; 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci/** 14658c2ecf20Sopenharmony_ci * fcoe_xmit() - Transmit a FCoE frame 14668c2ecf20Sopenharmony_ci * @lport: The local port that the frame is to be transmitted for 14678c2ecf20Sopenharmony_ci * @fp: The frame to be transmitted 14688c2ecf20Sopenharmony_ci * 14698c2ecf20Sopenharmony_ci * Return: 0 for success 14708c2ecf20Sopenharmony_ci */ 14718c2ecf20Sopenharmony_cistatic int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci int wlen; 14748c2ecf20Sopenharmony_ci u32 crc; 14758c2ecf20Sopenharmony_ci struct ethhdr *eh; 14768c2ecf20Sopenharmony_ci struct fcoe_crc_eof *cp; 14778c2ecf20Sopenharmony_ci struct sk_buff *skb; 14788c2ecf20Sopenharmony_ci struct fc_stats *stats; 14798c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 14808c2ecf20Sopenharmony_ci unsigned int hlen; /* header length implies the version */ 14818c2ecf20Sopenharmony_ci unsigned int tlen; /* trailer length */ 14828c2ecf20Sopenharmony_ci unsigned int elen; /* eth header, may include vlan */ 14838c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 14848c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 14858c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); 14868c2ecf20Sopenharmony_ci u8 sof, eof; 14878c2ecf20Sopenharmony_ci struct fcoe_hdr *hp; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci WARN_ON((fr_len(fp) % sizeof(u32)) != 0); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 14928c2ecf20Sopenharmony_ci skb = fp_skb(fp); 14938c2ecf20Sopenharmony_ci wlen = skb->len / FCOE_WORD_TO_BYTE; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (!lport->link_up) { 14968c2ecf20Sopenharmony_ci kfree_skb(skb); 14978c2ecf20Sopenharmony_ci return 0; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (unlikely(fh->fh_type == FC_TYPE_ELS) && 15018c2ecf20Sopenharmony_ci fcoe_ctlr_els_send(ctlr, lport, skb)) 15028c2ecf20Sopenharmony_ci return 0; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci sof = fr_sof(fp); 15058c2ecf20Sopenharmony_ci eof = fr_eof(fp); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci elen = sizeof(struct ethhdr); 15088c2ecf20Sopenharmony_ci hlen = sizeof(struct fcoe_hdr); 15098c2ecf20Sopenharmony_ci tlen = sizeof(struct fcoe_crc_eof); 15108c2ecf20Sopenharmony_ci wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci /* crc offload */ 15138c2ecf20Sopenharmony_ci if (likely(lport->crc_offload)) { 15148c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_PARTIAL; 15158c2ecf20Sopenharmony_ci skb->csum_start = skb_headroom(skb); 15168c2ecf20Sopenharmony_ci skb->csum_offset = skb->len; 15178c2ecf20Sopenharmony_ci crc = 0; 15188c2ecf20Sopenharmony_ci } else { 15198c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 15208c2ecf20Sopenharmony_ci crc = fcoe_fc_crc(fp); 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci /* copy port crc and eof to the skb buff */ 15248c2ecf20Sopenharmony_ci if (skb_is_nonlinear(skb)) { 15258c2ecf20Sopenharmony_ci skb_frag_t *frag; 15268c2ecf20Sopenharmony_ci if (fcoe_alloc_paged_crc_eof(skb, tlen)) { 15278c2ecf20Sopenharmony_ci kfree_skb(skb); 15288c2ecf20Sopenharmony_ci return -ENOMEM; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1]; 15318c2ecf20Sopenharmony_ci cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag); 15328c2ecf20Sopenharmony_ci } else { 15338c2ecf20Sopenharmony_ci cp = skb_put(skb, tlen); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci memset(cp, 0, sizeof(*cp)); 15378c2ecf20Sopenharmony_ci cp->fcoe_eof = eof; 15388c2ecf20Sopenharmony_ci cp->fcoe_crc32 = cpu_to_le32(~crc); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (skb_is_nonlinear(skb)) { 15418c2ecf20Sopenharmony_ci kunmap_atomic(cp); 15428c2ecf20Sopenharmony_ci cp = NULL; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* adjust skb network/transport offsets to match mac/fcoe/port */ 15468c2ecf20Sopenharmony_ci skb_push(skb, elen + hlen); 15478c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 15488c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 15498c2ecf20Sopenharmony_ci skb->mac_len = elen; 15508c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_FCOE); 15518c2ecf20Sopenharmony_ci skb->priority = fcoe->priority; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (is_vlan_dev(fcoe->netdev) && 15548c2ecf20Sopenharmony_ci fcoe->realdev->features & NETIF_F_HW_VLAN_CTAG_TX) { 15558c2ecf20Sopenharmony_ci /* must set skb->dev before calling vlan_put_tag */ 15568c2ecf20Sopenharmony_ci skb->dev = fcoe->realdev; 15578c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 15588c2ecf20Sopenharmony_ci vlan_dev_vlan_id(fcoe->netdev)); 15598c2ecf20Sopenharmony_ci } else 15608c2ecf20Sopenharmony_ci skb->dev = fcoe->netdev; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci /* fill up mac and fcoe headers */ 15638c2ecf20Sopenharmony_ci eh = eth_hdr(skb); 15648c2ecf20Sopenharmony_ci eh->h_proto = htons(ETH_P_FCOE); 15658c2ecf20Sopenharmony_ci memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN); 15668c2ecf20Sopenharmony_ci if (ctlr->map_dest) 15678c2ecf20Sopenharmony_ci memcpy(eh->h_dest + 3, fh->fh_d_id, 3); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN)) 15708c2ecf20Sopenharmony_ci memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN); 15718c2ecf20Sopenharmony_ci else 15728c2ecf20Sopenharmony_ci memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci hp = (struct fcoe_hdr *)(eh + 1); 15758c2ecf20Sopenharmony_ci memset(hp, 0, sizeof(*hp)); 15768c2ecf20Sopenharmony_ci if (FC_FCOE_VER) 15778c2ecf20Sopenharmony_ci FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); 15788c2ecf20Sopenharmony_ci hp->fcoe_sof = sof; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* fcoe lso, mss is in max_payload which is non-zero for FCP data */ 15818c2ecf20Sopenharmony_ci if (lport->seq_offload && fr_max_payload(fp)) { 15828c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = SKB_GSO_FCOE; 15838c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = fr_max_payload(fp); 15848c2ecf20Sopenharmony_ci } else { 15858c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_type = 0; 15868c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_size = 0; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci /* update tx stats: regardless if LLD fails */ 15898c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lport->stats, get_cpu()); 15908c2ecf20Sopenharmony_ci stats->TxFrames++; 15918c2ecf20Sopenharmony_ci stats->TxWords += wlen; 15928c2ecf20Sopenharmony_ci put_cpu(); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci /* send down to lld */ 15958c2ecf20Sopenharmony_ci fr_dev(fp) = lport; 15968c2ecf20Sopenharmony_ci fcoe_port_send(port, skb); 15978c2ecf20Sopenharmony_ci return 0; 15988c2ecf20Sopenharmony_ci} 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci/** 16018c2ecf20Sopenharmony_ci * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC 16028c2ecf20Sopenharmony_ci * @lport: The local port the frame was received on 16038c2ecf20Sopenharmony_ci * @fp: The received frame 16048c2ecf20Sopenharmony_ci * 16058c2ecf20Sopenharmony_ci * Return: 0 on passing filtering checks 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_cistatic inline int fcoe_filter_frames(struct fc_lport *lport, 16088c2ecf20Sopenharmony_ci struct fc_frame *fp) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 16118c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 16128c2ecf20Sopenharmony_ci struct fc_frame_header *fh; 16138c2ecf20Sopenharmony_ci struct sk_buff *skb = (struct sk_buff *)fp; 16148c2ecf20Sopenharmony_ci struct fc_stats *stats; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci /* 16178c2ecf20Sopenharmony_ci * We only check CRC if no offload is available and if it is 16188c2ecf20Sopenharmony_ci * it's solicited data, in which case, the FCP layer would 16198c2ecf20Sopenharmony_ci * check it during the copy. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ci if (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY) 16228c2ecf20Sopenharmony_ci fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; 16238c2ecf20Sopenharmony_ci else 16248c2ecf20Sopenharmony_ci fr_flags(fp) |= FCPHF_CRC_UNCHECKED; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci fh = fc_frame_header_get(fp); 16278c2ecf20Sopenharmony_ci if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP) 16288c2ecf20Sopenharmony_ci return 0; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci fcoe = ((struct fcoe_port *)lport_priv(lport))->priv; 16318c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 16328c2ecf20Sopenharmony_ci if (is_fip_mode(ctlr) && fc_frame_payload_op(fp) == ELS_LOGO && 16338c2ecf20Sopenharmony_ci ntoh24(fh->fh_s_id) == FC_FID_FLOGI) { 16348c2ecf20Sopenharmony_ci FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n"); 16358c2ecf20Sopenharmony_ci return -EINVAL; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED) || 16398c2ecf20Sopenharmony_ci le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) { 16408c2ecf20Sopenharmony_ci fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; 16418c2ecf20Sopenharmony_ci return 0; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lport->stats, get_cpu()); 16458c2ecf20Sopenharmony_ci stats->InvalidCRCCount++; 16468c2ecf20Sopenharmony_ci if (stats->InvalidCRCCount < 5) 16478c2ecf20Sopenharmony_ci printk(KERN_WARNING "fcoe: dropping frame with CRC error\n"); 16488c2ecf20Sopenharmony_ci put_cpu(); 16498c2ecf20Sopenharmony_ci return -EINVAL; 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/** 16538c2ecf20Sopenharmony_ci * fcoe_recv_frame() - process a single received frame 16548c2ecf20Sopenharmony_ci * @skb: frame to process 16558c2ecf20Sopenharmony_ci */ 16568c2ecf20Sopenharmony_cistatic void fcoe_recv_frame(struct sk_buff *skb) 16578c2ecf20Sopenharmony_ci{ 16588c2ecf20Sopenharmony_ci u32 fr_len; 16598c2ecf20Sopenharmony_ci struct fc_lport *lport; 16608c2ecf20Sopenharmony_ci struct fcoe_rcv_info *fr; 16618c2ecf20Sopenharmony_ci struct fc_stats *stats; 16628c2ecf20Sopenharmony_ci struct fcoe_crc_eof crc_eof; 16638c2ecf20Sopenharmony_ci struct fc_frame *fp; 16648c2ecf20Sopenharmony_ci struct fcoe_hdr *hp; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci fr = fcoe_dev_from_skb(skb); 16678c2ecf20Sopenharmony_ci lport = fr->fr_dev; 16688c2ecf20Sopenharmony_ci if (unlikely(!lport)) { 16698c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb\n"); 16708c2ecf20Sopenharmony_ci kfree_skb(skb); 16718c2ecf20Sopenharmony_ci return; 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(skb->dev, 16758c2ecf20Sopenharmony_ci "skb_info: len:%d data_len:%d head:%p data:%p tail:%p end:%p sum:%d dev:%s\n", 16768c2ecf20Sopenharmony_ci skb->len, skb->data_len, 16778c2ecf20Sopenharmony_ci skb->head, skb->data, skb_tail_pointer(skb), 16788c2ecf20Sopenharmony_ci skb_end_pointer(skb), skb->csum, 16798c2ecf20Sopenharmony_ci skb->dev ? skb->dev->name : "<NULL>"); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci skb_linearize(skb); /* check for skb_is_nonlinear is within skb_linearize */ 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* 16848c2ecf20Sopenharmony_ci * Frame length checks and setting up the header pointers 16858c2ecf20Sopenharmony_ci * was done in fcoe_rcv already. 16868c2ecf20Sopenharmony_ci */ 16878c2ecf20Sopenharmony_ci hp = (struct fcoe_hdr *) skb_network_header(skb); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lport->stats, get_cpu()); 16908c2ecf20Sopenharmony_ci if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { 16918c2ecf20Sopenharmony_ci if (stats->ErrorFrames < 5) 16928c2ecf20Sopenharmony_ci printk(KERN_WARNING "fcoe: FCoE version " 16938c2ecf20Sopenharmony_ci "mismatch: The frame has " 16948c2ecf20Sopenharmony_ci "version %x, but the " 16958c2ecf20Sopenharmony_ci "initiator supports version " 16968c2ecf20Sopenharmony_ci "%x\n", FC_FCOE_DECAPS_VER(hp), 16978c2ecf20Sopenharmony_ci FC_FCOE_VER); 16988c2ecf20Sopenharmony_ci goto drop; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(struct fcoe_hdr)); 17028c2ecf20Sopenharmony_ci fr_len = skb->len - sizeof(struct fcoe_crc_eof); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci stats->RxFrames++; 17058c2ecf20Sopenharmony_ci stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci fp = (struct fc_frame *)skb; 17088c2ecf20Sopenharmony_ci fc_frame_init(fp); 17098c2ecf20Sopenharmony_ci fr_dev(fp) = lport; 17108c2ecf20Sopenharmony_ci fr_sof(fp) = hp->fcoe_sof; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci /* Copy out the CRC and EOF trailer for access */ 17138c2ecf20Sopenharmony_ci if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) 17148c2ecf20Sopenharmony_ci goto drop; 17158c2ecf20Sopenharmony_ci fr_eof(fp) = crc_eof.fcoe_eof; 17168c2ecf20Sopenharmony_ci fr_crc(fp) = crc_eof.fcoe_crc32; 17178c2ecf20Sopenharmony_ci if (pskb_trim(skb, fr_len)) 17188c2ecf20Sopenharmony_ci goto drop; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci if (!fcoe_filter_frames(lport, fp)) { 17218c2ecf20Sopenharmony_ci put_cpu(); 17228c2ecf20Sopenharmony_ci fc_exch_recv(lport, fp); 17238c2ecf20Sopenharmony_ci return; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_cidrop: 17268c2ecf20Sopenharmony_ci stats->ErrorFrames++; 17278c2ecf20Sopenharmony_ci put_cpu(); 17288c2ecf20Sopenharmony_ci kfree_skb(skb); 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci/** 17328c2ecf20Sopenharmony_ci * fcoe_receive_work() - The per-CPU worker 17338c2ecf20Sopenharmony_ci * @work: The work struct 17348c2ecf20Sopenharmony_ci * 17358c2ecf20Sopenharmony_ci */ 17368c2ecf20Sopenharmony_cistatic void fcoe_receive_work(struct work_struct *work) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci struct fcoe_percpu_s *p; 17398c2ecf20Sopenharmony_ci struct sk_buff *skb; 17408c2ecf20Sopenharmony_ci struct sk_buff_head tmp; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci p = container_of(work, struct fcoe_percpu_s, work); 17438c2ecf20Sopenharmony_ci skb_queue_head_init(&tmp); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci spin_lock_bh(&p->fcoe_rx_list.lock); 17468c2ecf20Sopenharmony_ci skb_queue_splice_init(&p->fcoe_rx_list, &tmp); 17478c2ecf20Sopenharmony_ci spin_unlock_bh(&p->fcoe_rx_list.lock); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci if (!skb_queue_len(&tmp)) 17508c2ecf20Sopenharmony_ci return; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&tmp))) 17538c2ecf20Sopenharmony_ci fcoe_recv_frame(skb); 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci/** 17578c2ecf20Sopenharmony_ci * fcoe_dev_setup() - Setup the link change notification interface 17588c2ecf20Sopenharmony_ci */ 17598c2ecf20Sopenharmony_cistatic void fcoe_dev_setup(void) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci register_dcbevent_notifier(&dcb_notifier); 17628c2ecf20Sopenharmony_ci register_netdevice_notifier(&fcoe_notifier); 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci/** 17668c2ecf20Sopenharmony_ci * fcoe_dev_cleanup() - Cleanup the link change notification interface 17678c2ecf20Sopenharmony_ci */ 17688c2ecf20Sopenharmony_cistatic void fcoe_dev_cleanup(void) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci unregister_dcbevent_notifier(&dcb_notifier); 17718c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&fcoe_notifier); 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic struct fcoe_interface * 17758c2ecf20Sopenharmony_cifcoe_hostlist_lookup_realdev_port(struct net_device *netdev) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 17788c2ecf20Sopenharmony_ci struct net_device *real_dev; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci list_for_each_entry(fcoe, &fcoe_hostlist, list) { 17818c2ecf20Sopenharmony_ci if (is_vlan_dev(fcoe->netdev)) 17828c2ecf20Sopenharmony_ci real_dev = vlan_dev_real_dev(fcoe->netdev); 17838c2ecf20Sopenharmony_ci else 17848c2ecf20Sopenharmony_ci real_dev = fcoe->netdev; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (netdev == real_dev) 17878c2ecf20Sopenharmony_ci return fcoe; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci return NULL; 17908c2ecf20Sopenharmony_ci} 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_cistatic int fcoe_dcb_app_notification(struct notifier_block *notifier, 17938c2ecf20Sopenharmony_ci ulong event, void *ptr) 17948c2ecf20Sopenharmony_ci{ 17958c2ecf20Sopenharmony_ci struct dcb_app_type *entry = ptr; 17968c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 17978c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 17988c2ecf20Sopenharmony_ci struct net_device *netdev; 17998c2ecf20Sopenharmony_ci int prio; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE) 18028c2ecf20Sopenharmony_ci return NOTIFY_OK; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci netdev = dev_get_by_index(&init_net, entry->ifindex); 18058c2ecf20Sopenharmony_ci if (!netdev) 18068c2ecf20Sopenharmony_ci return NOTIFY_OK; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_realdev_port(netdev); 18098c2ecf20Sopenharmony_ci dev_put(netdev); 18108c2ecf20Sopenharmony_ci if (!fcoe) 18118c2ecf20Sopenharmony_ci return NOTIFY_OK; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (entry->dcbx & DCB_CAP_DCBX_VER_CEE) 18168c2ecf20Sopenharmony_ci prio = ffs(entry->app.priority) - 1; 18178c2ecf20Sopenharmony_ci else 18188c2ecf20Sopenharmony_ci prio = entry->app.priority; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci if (prio < 0) 18218c2ecf20Sopenharmony_ci return NOTIFY_OK; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (entry->app.protocol == ETH_P_FIP || 18248c2ecf20Sopenharmony_ci entry->app.protocol == ETH_P_FCOE) 18258c2ecf20Sopenharmony_ci ctlr->priority = prio; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (entry->app.protocol == ETH_P_FCOE) 18288c2ecf20Sopenharmony_ci fcoe->priority = prio; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci return NOTIFY_OK; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci/** 18348c2ecf20Sopenharmony_ci * fcoe_device_notification() - Handler for net device events 18358c2ecf20Sopenharmony_ci * @notifier: The context of the notification 18368c2ecf20Sopenharmony_ci * @event: The type of event 18378c2ecf20Sopenharmony_ci * @ptr: The net device that the event was on 18388c2ecf20Sopenharmony_ci * 18398c2ecf20Sopenharmony_ci * This function is called by the Ethernet driver in case of link change event. 18408c2ecf20Sopenharmony_ci * 18418c2ecf20Sopenharmony_ci * Returns: 0 for success 18428c2ecf20Sopenharmony_ci */ 18438c2ecf20Sopenharmony_cistatic int fcoe_device_notification(struct notifier_block *notifier, 18448c2ecf20Sopenharmony_ci ulong event, void *ptr) 18458c2ecf20Sopenharmony_ci{ 18468c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *cdev; 18478c2ecf20Sopenharmony_ci struct fc_lport *lport = NULL; 18488c2ecf20Sopenharmony_ci struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 18498c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 18508c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 18518c2ecf20Sopenharmony_ci struct fc_stats *stats; 18528c2ecf20Sopenharmony_ci u32 link_possible = 1; 18538c2ecf20Sopenharmony_ci u32 mfs; 18548c2ecf20Sopenharmony_ci int rc = NOTIFY_OK; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci list_for_each_entry(fcoe, &fcoe_hostlist, list) { 18578c2ecf20Sopenharmony_ci if (fcoe->netdev == netdev) { 18588c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 18598c2ecf20Sopenharmony_ci lport = ctlr->lp; 18608c2ecf20Sopenharmony_ci break; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci if (!lport) { 18648c2ecf20Sopenharmony_ci rc = NOTIFY_DONE; 18658c2ecf20Sopenharmony_ci goto out; 18668c2ecf20Sopenharmony_ci } 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci switch (event) { 18698c2ecf20Sopenharmony_ci case NETDEV_DOWN: 18708c2ecf20Sopenharmony_ci case NETDEV_GOING_DOWN: 18718c2ecf20Sopenharmony_ci link_possible = 0; 18728c2ecf20Sopenharmony_ci break; 18738c2ecf20Sopenharmony_ci case NETDEV_UP: 18748c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 18758c2ecf20Sopenharmony_ci break; 18768c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 18778c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_FCOE_MTU) 18788c2ecf20Sopenharmony_ci break; 18798c2ecf20Sopenharmony_ci mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + 18808c2ecf20Sopenharmony_ci sizeof(struct fcoe_crc_eof)); 18818c2ecf20Sopenharmony_ci if (mfs >= FC_MIN_MAX_FRAME) 18828c2ecf20Sopenharmony_ci fc_set_mfs(lport, mfs); 18838c2ecf20Sopenharmony_ci break; 18848c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 18858c2ecf20Sopenharmony_ci break; 18868c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 18878c2ecf20Sopenharmony_ci list_del(&fcoe->list); 18888c2ecf20Sopenharmony_ci fcoe_vport_remove(lport); 18898c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 18908c2ecf20Sopenharmony_ci fcoe_if_destroy(lport); 18918c2ecf20Sopenharmony_ci if (!fcoe->removed) 18928c2ecf20Sopenharmony_ci fcoe_interface_remove(fcoe); 18938c2ecf20Sopenharmony_ci fcoe_interface_cleanup(fcoe); 18948c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 18958c2ecf20Sopenharmony_ci fcoe_ctlr_device_delete(fcoe_ctlr_to_ctlr_dev(ctlr)); 18968c2ecf20Sopenharmony_ci goto out; 18978c2ecf20Sopenharmony_ci break; 18988c2ecf20Sopenharmony_ci case NETDEV_FEAT_CHANGE: 18998c2ecf20Sopenharmony_ci fcoe_netdev_features_change(lport, netdev); 19008c2ecf20Sopenharmony_ci break; 19018c2ecf20Sopenharmony_ci default: 19028c2ecf20Sopenharmony_ci FCOE_NETDEV_DBG(netdev, "Unknown event %ld " 19038c2ecf20Sopenharmony_ci "from netdev netlink\n", event); 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci fcoe_link_speed_update(lport); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci cdev = fcoe_ctlr_to_ctlr_dev(ctlr); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci if (link_possible && !fcoe_link_ok(lport)) { 19118c2ecf20Sopenharmony_ci switch (cdev->enabled) { 19128c2ecf20Sopenharmony_ci case FCOE_CTLR_DISABLED: 19138c2ecf20Sopenharmony_ci pr_info("Link up while interface is disabled.\n"); 19148c2ecf20Sopenharmony_ci break; 19158c2ecf20Sopenharmony_ci case FCOE_CTLR_ENABLED: 19168c2ecf20Sopenharmony_ci case FCOE_CTLR_UNUSED: 19178c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(ctlr); 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci } else if (fcoe_ctlr_link_down(ctlr)) { 19208c2ecf20Sopenharmony_ci switch (cdev->enabled) { 19218c2ecf20Sopenharmony_ci case FCOE_CTLR_DISABLED: 19228c2ecf20Sopenharmony_ci pr_info("Link down while interface is disabled.\n"); 19238c2ecf20Sopenharmony_ci break; 19248c2ecf20Sopenharmony_ci case FCOE_CTLR_ENABLED: 19258c2ecf20Sopenharmony_ci case FCOE_CTLR_UNUSED: 19268c2ecf20Sopenharmony_ci stats = per_cpu_ptr(lport->stats, get_cpu()); 19278c2ecf20Sopenharmony_ci stats->LinkFailureCount++; 19288c2ecf20Sopenharmony_ci put_cpu(); 19298c2ecf20Sopenharmony_ci fcoe_clean_pending_queue(lport); 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci } 19328c2ecf20Sopenharmony_ciout: 19338c2ecf20Sopenharmony_ci return rc; 19348c2ecf20Sopenharmony_ci} 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci/** 19378c2ecf20Sopenharmony_ci * fcoe_disable() - Disables a FCoE interface 19388c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 19398c2ecf20Sopenharmony_ci * 19408c2ecf20Sopenharmony_ci * Called from fcoe transport. 19418c2ecf20Sopenharmony_ci * 19428c2ecf20Sopenharmony_ci * Returns: 0 for success 19438c2ecf20Sopenharmony_ci * 19448c2ecf20Sopenharmony_ci * Deprecated: use fcoe_ctlr_enabled() 19458c2ecf20Sopenharmony_ci */ 19468c2ecf20Sopenharmony_cistatic int fcoe_disable(struct net_device *netdev) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 19498c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 19508c2ecf20Sopenharmony_ci int rc = 0; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci rtnl_lock(); 19558c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_port(netdev); 19568c2ecf20Sopenharmony_ci rtnl_unlock(); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (fcoe) { 19598c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 19608c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(ctlr); 19618c2ecf20Sopenharmony_ci fcoe_clean_pending_queue(ctlr->lp); 19628c2ecf20Sopenharmony_ci } else 19638c2ecf20Sopenharmony_ci rc = -ENODEV; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 19668c2ecf20Sopenharmony_ci return rc; 19678c2ecf20Sopenharmony_ci} 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci/** 19708c2ecf20Sopenharmony_ci * fcoe_enable() - Enables a FCoE interface 19718c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 19728c2ecf20Sopenharmony_ci * 19738c2ecf20Sopenharmony_ci * Called from fcoe transport. 19748c2ecf20Sopenharmony_ci * 19758c2ecf20Sopenharmony_ci * Returns: 0 for success 19768c2ecf20Sopenharmony_ci */ 19778c2ecf20Sopenharmony_cistatic int fcoe_enable(struct net_device *netdev) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 19808c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 19818c2ecf20Sopenharmony_ci int rc = 0; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 19848c2ecf20Sopenharmony_ci rtnl_lock(); 19858c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_port(netdev); 19868c2ecf20Sopenharmony_ci rtnl_unlock(); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci if (!fcoe) { 19898c2ecf20Sopenharmony_ci rc = -ENODEV; 19908c2ecf20Sopenharmony_ci goto out; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (!fcoe_link_ok(ctlr->lp)) 19968c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(ctlr); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ciout: 19998c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 20008c2ecf20Sopenharmony_ci return rc; 20018c2ecf20Sopenharmony_ci} 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci/** 20048c2ecf20Sopenharmony_ci * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller 20058c2ecf20Sopenharmony_ci * @cdev: The FCoE Controller that is being enabled or disabled 20068c2ecf20Sopenharmony_ci * 20078c2ecf20Sopenharmony_ci * fcoe_sysfs will ensure that the state of 'enabled' has 20088c2ecf20Sopenharmony_ci * changed, so no checking is necessary here. This routine simply 20098c2ecf20Sopenharmony_ci * calls fcoe_enable or fcoe_disable, both of which are deprecated. 20108c2ecf20Sopenharmony_ci * When those routines are removed the functionality can be merged 20118c2ecf20Sopenharmony_ci * here. 20128c2ecf20Sopenharmony_ci */ 20138c2ecf20Sopenharmony_cistatic int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); 20168c2ecf20Sopenharmony_ci struct fc_lport *lport = ctlr->lp; 20178c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe_netdev(lport); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci switch (cdev->enabled) { 20208c2ecf20Sopenharmony_ci case FCOE_CTLR_ENABLED: 20218c2ecf20Sopenharmony_ci return fcoe_enable(netdev); 20228c2ecf20Sopenharmony_ci case FCOE_CTLR_DISABLED: 20238c2ecf20Sopenharmony_ci return fcoe_disable(netdev); 20248c2ecf20Sopenharmony_ci case FCOE_CTLR_UNUSED: 20258c2ecf20Sopenharmony_ci default: 20268c2ecf20Sopenharmony_ci return -ENOTSUPP; 20278c2ecf20Sopenharmony_ci }; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci/** 20318c2ecf20Sopenharmony_ci * fcoe_ctlr_mode() - Switch FIP mode 20328c2ecf20Sopenharmony_ci * @ctlr_dev: The FCoE Controller that is being modified 20338c2ecf20Sopenharmony_ci * 20348c2ecf20Sopenharmony_ci * When the FIP mode has been changed we need to update 20358c2ecf20Sopenharmony_ci * the multicast addresses to ensure we get the correct 20368c2ecf20Sopenharmony_ci * frames. 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_cistatic void fcoe_ctlr_mode(struct fcoe_ctlr_device *ctlr_dev) 20398c2ecf20Sopenharmony_ci{ 20408c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); 20418c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (ctlr_dev->mode == FIP_CONN_TYPE_VN2VN && 20448c2ecf20Sopenharmony_ci ctlr->mode != FIP_MODE_VN2VN) { 20458c2ecf20Sopenharmony_ci dev_mc_del(fcoe->netdev, FIP_ALL_ENODE_MACS); 20468c2ecf20Sopenharmony_ci dev_mc_add(fcoe->netdev, FIP_ALL_VN2VN_MACS); 20478c2ecf20Sopenharmony_ci dev_mc_add(fcoe->netdev, FIP_ALL_P2P_MACS); 20488c2ecf20Sopenharmony_ci } else if (ctlr->mode != FIP_MODE_FABRIC) { 20498c2ecf20Sopenharmony_ci dev_mc_del(fcoe->netdev, FIP_ALL_VN2VN_MACS); 20508c2ecf20Sopenharmony_ci dev_mc_del(fcoe->netdev, FIP_ALL_P2P_MACS); 20518c2ecf20Sopenharmony_ci dev_mc_add(fcoe->netdev, FIP_ALL_ENODE_MACS); 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci fcoe_ctlr_set_fip_mode(ctlr_dev); 20548c2ecf20Sopenharmony_ci} 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci/** 20578c2ecf20Sopenharmony_ci * fcoe_destroy() - Destroy a FCoE interface 20588c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 20598c2ecf20Sopenharmony_ci * 20608c2ecf20Sopenharmony_ci * Called from fcoe transport 20618c2ecf20Sopenharmony_ci * 20628c2ecf20Sopenharmony_ci * Returns: 0 for success 20638c2ecf20Sopenharmony_ci */ 20648c2ecf20Sopenharmony_cistatic int fcoe_destroy(struct net_device *netdev) 20658c2ecf20Sopenharmony_ci{ 20668c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 20678c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 20688c2ecf20Sopenharmony_ci struct fc_lport *lport; 20698c2ecf20Sopenharmony_ci struct fcoe_port *port; 20708c2ecf20Sopenharmony_ci int rc = 0; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 20738c2ecf20Sopenharmony_ci rtnl_lock(); 20748c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_port(netdev); 20758c2ecf20Sopenharmony_ci if (!fcoe) { 20768c2ecf20Sopenharmony_ci rc = -ENODEV; 20778c2ecf20Sopenharmony_ci goto out_nodev; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 20808c2ecf20Sopenharmony_ci lport = ctlr->lp; 20818c2ecf20Sopenharmony_ci port = lport_priv(lport); 20828c2ecf20Sopenharmony_ci list_del(&fcoe->list); 20838c2ecf20Sopenharmony_ci queue_work(fcoe_wq, &port->destroy_work); 20848c2ecf20Sopenharmony_ciout_nodev: 20858c2ecf20Sopenharmony_ci rtnl_unlock(); 20868c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 20878c2ecf20Sopenharmony_ci return rc; 20888c2ecf20Sopenharmony_ci} 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci/** 20918c2ecf20Sopenharmony_ci * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context 20928c2ecf20Sopenharmony_ci * @work: Handle to the FCoE port to be destroyed 20938c2ecf20Sopenharmony_ci */ 20948c2ecf20Sopenharmony_cistatic void fcoe_destroy_work(struct work_struct *work) 20958c2ecf20Sopenharmony_ci{ 20968c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *cdev; 20978c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 20988c2ecf20Sopenharmony_ci struct fcoe_port *port; 20998c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci port = container_of(work, struct fcoe_port, destroy_work); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci fcoe_vport_remove(port->lport); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci fcoe = port->priv; 21088c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 21098c2ecf20Sopenharmony_ci cdev = fcoe_ctlr_to_ctlr_dev(ctlr); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci rtnl_lock(); 21128c2ecf20Sopenharmony_ci fcoe_if_destroy(port->lport); 21138c2ecf20Sopenharmony_ci if (!fcoe->removed) 21148c2ecf20Sopenharmony_ci fcoe_interface_remove(fcoe); 21158c2ecf20Sopenharmony_ci rtnl_unlock(); 21168c2ecf20Sopenharmony_ci fcoe_interface_cleanup(fcoe); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci fcoe_ctlr_device_delete(cdev); 21218c2ecf20Sopenharmony_ci} 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci/** 21248c2ecf20Sopenharmony_ci * fcoe_match() - Check if the FCoE is supported on the given netdevice 21258c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 21268c2ecf20Sopenharmony_ci * 21278c2ecf20Sopenharmony_ci * Called from fcoe transport. 21288c2ecf20Sopenharmony_ci * 21298c2ecf20Sopenharmony_ci * Returns: always returns true as this is the default FCoE transport, 21308c2ecf20Sopenharmony_ci * i.e., support all netdevs. 21318c2ecf20Sopenharmony_ci */ 21328c2ecf20Sopenharmony_cistatic bool fcoe_match(struct net_device *netdev) 21338c2ecf20Sopenharmony_ci{ 21348c2ecf20Sopenharmony_ci return true; 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci/** 21388c2ecf20Sopenharmony_ci * fcoe_dcb_create() - Initialize DCB attributes and hooks 21398c2ecf20Sopenharmony_ci * @fcoe: The new FCoE interface 21408c2ecf20Sopenharmony_ci */ 21418c2ecf20Sopenharmony_cistatic void fcoe_dcb_create(struct fcoe_interface *fcoe) 21428c2ecf20Sopenharmony_ci{ 21438c2ecf20Sopenharmony_ci int ctlr_prio = TC_PRIO_BESTEFFORT; 21448c2ecf20Sopenharmony_ci int fcoe_prio = TC_PRIO_INTERACTIVE; 21458c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); 21468c2ecf20Sopenharmony_ci#ifdef CONFIG_DCB 21478c2ecf20Sopenharmony_ci int dcbx; 21488c2ecf20Sopenharmony_ci u8 fup, up; 21498c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->realdev; 21508c2ecf20Sopenharmony_ci struct dcb_app app = { 21518c2ecf20Sopenharmony_ci .priority = 0, 21528c2ecf20Sopenharmony_ci .protocol = ETH_P_FCOE 21538c2ecf20Sopenharmony_ci }; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci /* setup DCB priority attributes. */ 21568c2ecf20Sopenharmony_ci if (netdev && netdev->dcbnl_ops && netdev->dcbnl_ops->getdcbx) { 21578c2ecf20Sopenharmony_ci dcbx = netdev->dcbnl_ops->getdcbx(netdev); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci if (dcbx & DCB_CAP_DCBX_VER_IEEE) { 21608c2ecf20Sopenharmony_ci app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; 21618c2ecf20Sopenharmony_ci up = dcb_ieee_getapp_mask(netdev, &app); 21628c2ecf20Sopenharmony_ci app.protocol = ETH_P_FIP; 21638c2ecf20Sopenharmony_ci fup = dcb_ieee_getapp_mask(netdev, &app); 21648c2ecf20Sopenharmony_ci } else { 21658c2ecf20Sopenharmony_ci app.selector = DCB_APP_IDTYPE_ETHTYPE; 21668c2ecf20Sopenharmony_ci up = dcb_getapp(netdev, &app); 21678c2ecf20Sopenharmony_ci app.protocol = ETH_P_FIP; 21688c2ecf20Sopenharmony_ci fup = dcb_getapp(netdev, &app); 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci fcoe_prio = ffs(up) ? ffs(up) - 1 : 0; 21728c2ecf20Sopenharmony_ci ctlr_prio = ffs(fup) ? ffs(fup) - 1 : fcoe_prio; 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci#endif 21758c2ecf20Sopenharmony_ci fcoe->priority = fcoe_prio; 21768c2ecf20Sopenharmony_ci ctlr->priority = ctlr_prio; 21778c2ecf20Sopenharmony_ci} 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_cienum fcoe_create_link_state { 21808c2ecf20Sopenharmony_ci FCOE_CREATE_LINK_DOWN, 21818c2ecf20Sopenharmony_ci FCOE_CREATE_LINK_UP, 21828c2ecf20Sopenharmony_ci}; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci/** 21858c2ecf20Sopenharmony_ci * _fcoe_create() - (internal) Create a fcoe interface 21868c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 21878c2ecf20Sopenharmony_ci * @fip_mode: The FIP mode for this creation 21888c2ecf20Sopenharmony_ci * @link_state: The ctlr link state on creation 21898c2ecf20Sopenharmony_ci * 21908c2ecf20Sopenharmony_ci * Called from either the libfcoe 'create' module parameter 21918c2ecf20Sopenharmony_ci * via fcoe_create or from fcoe_syfs's ctlr_create file. 21928c2ecf20Sopenharmony_ci * 21938c2ecf20Sopenharmony_ci * libfcoe's 'create' module parameter is deprecated so some 21948c2ecf20Sopenharmony_ci * consolidation of code can be done when that interface is 21958c2ecf20Sopenharmony_ci * removed. 21968c2ecf20Sopenharmony_ci */ 21978c2ecf20Sopenharmony_cistatic int _fcoe_create(struct net_device *netdev, enum fip_mode fip_mode, 21988c2ecf20Sopenharmony_ci enum fcoe_create_link_state link_state) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci int rc = 0; 22018c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *ctlr_dev; 22028c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 22038c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 22048c2ecf20Sopenharmony_ci struct fc_lport *lport; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 22078c2ecf20Sopenharmony_ci rtnl_lock(); 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci /* look for existing lport */ 22108c2ecf20Sopenharmony_ci if (fcoe_hostlist_lookup(netdev)) { 22118c2ecf20Sopenharmony_ci rc = -EEXIST; 22128c2ecf20Sopenharmony_ci goto out_nodev; 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci fcoe = fcoe_interface_create(netdev, fip_mode); 22168c2ecf20Sopenharmony_ci if (IS_ERR(fcoe)) { 22178c2ecf20Sopenharmony_ci rc = PTR_ERR(fcoe); 22188c2ecf20Sopenharmony_ci goto out_nodev; 22198c2ecf20Sopenharmony_ci } 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 22228c2ecf20Sopenharmony_ci ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr); 22238c2ecf20Sopenharmony_ci lport = fcoe_if_create(fcoe, &ctlr_dev->dev, 0); 22248c2ecf20Sopenharmony_ci if (IS_ERR(lport)) { 22258c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", 22268c2ecf20Sopenharmony_ci netdev->name); 22278c2ecf20Sopenharmony_ci rc = -EIO; 22288c2ecf20Sopenharmony_ci if (!fcoe->removed) 22298c2ecf20Sopenharmony_ci fcoe_interface_remove(fcoe); 22308c2ecf20Sopenharmony_ci rtnl_unlock(); 22318c2ecf20Sopenharmony_ci fcoe_interface_cleanup(fcoe); 22328c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 22338c2ecf20Sopenharmony_ci fcoe_ctlr_device_delete(ctlr_dev); 22348c2ecf20Sopenharmony_ci return rc; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* Make this the "master" N_Port */ 22388c2ecf20Sopenharmony_ci ctlr->lp = lport; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci /* setup DCB priority attributes. */ 22418c2ecf20Sopenharmony_ci fcoe_dcb_create(fcoe); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci /* start FIP Discovery and FLOGI */ 22448c2ecf20Sopenharmony_ci lport->boot_time = jiffies; 22458c2ecf20Sopenharmony_ci fc_fabric_login(lport); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci /* 22488c2ecf20Sopenharmony_ci * If the fcoe_ctlr_device is to be set to DISABLED 22498c2ecf20Sopenharmony_ci * it must be done after the lport is added to the 22508c2ecf20Sopenharmony_ci * hostlist, but before the rtnl_lock is released. 22518c2ecf20Sopenharmony_ci * This is because the rtnl_lock protects the 22528c2ecf20Sopenharmony_ci * hostlist that fcoe_device_notification uses. If 22538c2ecf20Sopenharmony_ci * the FCoE Controller is intended to be created 22548c2ecf20Sopenharmony_ci * DISABLED then 'enabled' needs to be considered 22558c2ecf20Sopenharmony_ci * handling link events. 'enabled' must be set 22568c2ecf20Sopenharmony_ci * before the lport can be found in the hostlist 22578c2ecf20Sopenharmony_ci * when a link up event is received. 22588c2ecf20Sopenharmony_ci */ 22598c2ecf20Sopenharmony_ci if (link_state == FCOE_CREATE_LINK_UP) 22608c2ecf20Sopenharmony_ci ctlr_dev->enabled = FCOE_CTLR_ENABLED; 22618c2ecf20Sopenharmony_ci else 22628c2ecf20Sopenharmony_ci ctlr_dev->enabled = FCOE_CTLR_DISABLED; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci if (link_state == FCOE_CREATE_LINK_UP && 22658c2ecf20Sopenharmony_ci !fcoe_link_ok(lport)) { 22668c2ecf20Sopenharmony_ci rtnl_unlock(); 22678c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(ctlr); 22688c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 22698c2ecf20Sopenharmony_ci return rc; 22708c2ecf20Sopenharmony_ci } 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ciout_nodev: 22738c2ecf20Sopenharmony_ci rtnl_unlock(); 22748c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci return rc; 22778c2ecf20Sopenharmony_ci} 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci/** 22808c2ecf20Sopenharmony_ci * fcoe_create() - Create a fcoe interface 22818c2ecf20Sopenharmony_ci * @netdev : The net_device object the Ethernet interface to create on 22828c2ecf20Sopenharmony_ci * @fip_mode: The FIP mode for this creation 22838c2ecf20Sopenharmony_ci * 22848c2ecf20Sopenharmony_ci * Called from fcoe transport 22858c2ecf20Sopenharmony_ci * 22868c2ecf20Sopenharmony_ci * Returns: 0 for success 22878c2ecf20Sopenharmony_ci */ 22888c2ecf20Sopenharmony_cistatic int fcoe_create(struct net_device *netdev, enum fip_mode fip_mode) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP); 22918c2ecf20Sopenharmony_ci} 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci/** 22948c2ecf20Sopenharmony_ci * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs 22958c2ecf20Sopenharmony_ci * @netdev: The net_device to be used by the allocated FCoE Controller 22968c2ecf20Sopenharmony_ci * 22978c2ecf20Sopenharmony_ci * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr 22988c2ecf20Sopenharmony_ci * in a link_down state. The allows the user an opportunity to configure 22998c2ecf20Sopenharmony_ci * the FCoE Controller from sysfs before enabling the FCoE Controller. 23008c2ecf20Sopenharmony_ci * 23018c2ecf20Sopenharmony_ci * Creating in with this routine starts the FCoE Controller in Fabric 23028c2ecf20Sopenharmony_ci * mode. The user can change to VN2VN or another mode before enabling. 23038c2ecf20Sopenharmony_ci */ 23048c2ecf20Sopenharmony_cistatic int fcoe_ctlr_alloc(struct net_device *netdev) 23058c2ecf20Sopenharmony_ci{ 23068c2ecf20Sopenharmony_ci return _fcoe_create(netdev, FIP_MODE_FABRIC, 23078c2ecf20Sopenharmony_ci FCOE_CREATE_LINK_DOWN); 23088c2ecf20Sopenharmony_ci} 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci/** 23118c2ecf20Sopenharmony_ci * fcoe_link_ok() - Check if the link is OK for a local port 23128c2ecf20Sopenharmony_ci * @lport: The local port to check link on 23138c2ecf20Sopenharmony_ci * 23148c2ecf20Sopenharmony_ci * Returns: 0 if link is UP and OK, -1 if not 23158c2ecf20Sopenharmony_ci * 23168c2ecf20Sopenharmony_ci */ 23178c2ecf20Sopenharmony_cistatic int fcoe_link_ok(struct fc_lport *lport) 23188c2ecf20Sopenharmony_ci{ 23198c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe_netdev(lport); 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci if (netif_oper_up(netdev)) 23228c2ecf20Sopenharmony_ci return 0; 23238c2ecf20Sopenharmony_ci return -1; 23248c2ecf20Sopenharmony_ci} 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci/** 23278c2ecf20Sopenharmony_ci * fcoe_percpu_clean() - Clear all pending skbs for an local port 23288c2ecf20Sopenharmony_ci * @lport: The local port whose skbs are to be cleared 23298c2ecf20Sopenharmony_ci * 23308c2ecf20Sopenharmony_ci * Must be called with fcoe_create_mutex held to single-thread completion. 23318c2ecf20Sopenharmony_ci * 23328c2ecf20Sopenharmony_ci * This flushes the pending skbs by flush the work item for each CPU. The work 23338c2ecf20Sopenharmony_ci * item on each possible CPU is flushed because we may have used the per-CPU 23348c2ecf20Sopenharmony_ci * struct of an offline CPU. 23358c2ecf20Sopenharmony_ci */ 23368c2ecf20Sopenharmony_cistatic void fcoe_percpu_clean(struct fc_lport *lport) 23378c2ecf20Sopenharmony_ci{ 23388c2ecf20Sopenharmony_ci struct fcoe_percpu_s *pp; 23398c2ecf20Sopenharmony_ci unsigned int cpu; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 23428c2ecf20Sopenharmony_ci pp = &per_cpu(fcoe_percpu, cpu); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci flush_work(&pp->work); 23458c2ecf20Sopenharmony_ci } 23468c2ecf20Sopenharmony_ci} 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci/** 23498c2ecf20Sopenharmony_ci * fcoe_reset() - Reset a local port 23508c2ecf20Sopenharmony_ci * @shost: The SCSI host associated with the local port to be reset 23518c2ecf20Sopenharmony_ci * 23528c2ecf20Sopenharmony_ci * Returns: Always 0 (return value required by FC transport template) 23538c2ecf20Sopenharmony_ci */ 23548c2ecf20Sopenharmony_cistatic int fcoe_reset(struct Scsi_Host *shost) 23558c2ecf20Sopenharmony_ci{ 23568c2ecf20Sopenharmony_ci struct fc_lport *lport = shost_priv(shost); 23578c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 23588c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 23598c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); 23608c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci fcoe_ctlr_link_down(ctlr); 23638c2ecf20Sopenharmony_ci fcoe_clean_pending_queue(ctlr->lp); 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci if (cdev->enabled != FCOE_CTLR_DISABLED && 23668c2ecf20Sopenharmony_ci !fcoe_link_ok(ctlr->lp)) 23678c2ecf20Sopenharmony_ci fcoe_ctlr_link_up(ctlr); 23688c2ecf20Sopenharmony_ci return 0; 23698c2ecf20Sopenharmony_ci} 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci/** 23728c2ecf20Sopenharmony_ci * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device 23738c2ecf20Sopenharmony_ci * @netdev: The net device used as a key 23748c2ecf20Sopenharmony_ci * 23758c2ecf20Sopenharmony_ci * Locking: Must be called with the RNL mutex held. 23768c2ecf20Sopenharmony_ci * 23778c2ecf20Sopenharmony_ci * Returns: NULL or the FCoE interface 23788c2ecf20Sopenharmony_ci */ 23798c2ecf20Sopenharmony_cistatic struct fcoe_interface * 23808c2ecf20Sopenharmony_cifcoe_hostlist_lookup_port(const struct net_device *netdev) 23818c2ecf20Sopenharmony_ci{ 23828c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci list_for_each_entry(fcoe, &fcoe_hostlist, list) { 23858c2ecf20Sopenharmony_ci if (fcoe->netdev == netdev) 23868c2ecf20Sopenharmony_ci return fcoe; 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci return NULL; 23898c2ecf20Sopenharmony_ci} 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci/** 23928c2ecf20Sopenharmony_ci * fcoe_hostlist_lookup() - Find the local port associated with a 23938c2ecf20Sopenharmony_ci * given net device 23948c2ecf20Sopenharmony_ci * @netdev: The netdevice used as a key 23958c2ecf20Sopenharmony_ci * 23968c2ecf20Sopenharmony_ci * Locking: Must be called with the RTNL mutex held 23978c2ecf20Sopenharmony_ci * 23988c2ecf20Sopenharmony_ci * Returns: NULL or the local port 23998c2ecf20Sopenharmony_ci */ 24008c2ecf20Sopenharmony_cistatic struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) 24018c2ecf20Sopenharmony_ci{ 24028c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 24038c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_port(netdev); 24068c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 24078c2ecf20Sopenharmony_ci return (fcoe) ? ctlr->lp : NULL; 24088c2ecf20Sopenharmony_ci} 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci/** 24118c2ecf20Sopenharmony_ci * fcoe_hostlist_add() - Add the FCoE interface identified by a local 24128c2ecf20Sopenharmony_ci * port to the hostlist 24138c2ecf20Sopenharmony_ci * @lport: The local port that identifies the FCoE interface to be added 24148c2ecf20Sopenharmony_ci * 24158c2ecf20Sopenharmony_ci * Locking: must be called with the RTNL mutex held 24168c2ecf20Sopenharmony_ci * 24178c2ecf20Sopenharmony_ci * Returns: 0 for success 24188c2ecf20Sopenharmony_ci */ 24198c2ecf20Sopenharmony_cistatic int fcoe_hostlist_add(const struct fc_lport *lport) 24208c2ecf20Sopenharmony_ci{ 24218c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 24228c2ecf20Sopenharmony_ci struct fcoe_port *port; 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); 24258c2ecf20Sopenharmony_ci if (!fcoe) { 24268c2ecf20Sopenharmony_ci port = lport_priv(lport); 24278c2ecf20Sopenharmony_ci fcoe = port->priv; 24288c2ecf20Sopenharmony_ci list_add_tail(&fcoe->list, &fcoe_hostlist); 24298c2ecf20Sopenharmony_ci } 24308c2ecf20Sopenharmony_ci return 0; 24318c2ecf20Sopenharmony_ci} 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci/** 24348c2ecf20Sopenharmony_ci * fcoe_hostlist_del() - Remove the FCoE interface identified by a local 24358c2ecf20Sopenharmony_ci * port to the hostlist 24368c2ecf20Sopenharmony_ci * @lport: The local port that identifies the FCoE interface to be added 24378c2ecf20Sopenharmony_ci * 24388c2ecf20Sopenharmony_ci * Locking: must be called with the RTNL mutex held 24398c2ecf20Sopenharmony_ci * 24408c2ecf20Sopenharmony_ci */ 24418c2ecf20Sopenharmony_cistatic void fcoe_hostlist_del(const struct fc_lport *lport) 24428c2ecf20Sopenharmony_ci{ 24438c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe; 24448c2ecf20Sopenharmony_ci struct fcoe_port *port; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci port = lport_priv(lport); 24478c2ecf20Sopenharmony_ci fcoe = port->priv; 24488c2ecf20Sopenharmony_ci list_del(&fcoe->list); 24498c2ecf20Sopenharmony_ci return; 24508c2ecf20Sopenharmony_ci} 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_cistatic struct fcoe_transport fcoe_sw_transport = { 24538c2ecf20Sopenharmony_ci .name = {FCOE_TRANSPORT_DEFAULT}, 24548c2ecf20Sopenharmony_ci .attached = false, 24558c2ecf20Sopenharmony_ci .list = LIST_HEAD_INIT(fcoe_sw_transport.list), 24568c2ecf20Sopenharmony_ci .match = fcoe_match, 24578c2ecf20Sopenharmony_ci .alloc = fcoe_ctlr_alloc, 24588c2ecf20Sopenharmony_ci .create = fcoe_create, 24598c2ecf20Sopenharmony_ci .destroy = fcoe_destroy, 24608c2ecf20Sopenharmony_ci .enable = fcoe_enable, 24618c2ecf20Sopenharmony_ci .disable = fcoe_disable, 24628c2ecf20Sopenharmony_ci}; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci/** 24658c2ecf20Sopenharmony_ci * fcoe_init() - Initialize fcoe.ko 24668c2ecf20Sopenharmony_ci * 24678c2ecf20Sopenharmony_ci * Returns: 0 on success, or a negative value on failure 24688c2ecf20Sopenharmony_ci */ 24698c2ecf20Sopenharmony_cistatic int __init fcoe_init(void) 24708c2ecf20Sopenharmony_ci{ 24718c2ecf20Sopenharmony_ci struct fcoe_percpu_s *p; 24728c2ecf20Sopenharmony_ci unsigned int cpu; 24738c2ecf20Sopenharmony_ci int rc = 0; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci fcoe_wq = alloc_workqueue("fcoe", 0, 0); 24768c2ecf20Sopenharmony_ci if (!fcoe_wq) 24778c2ecf20Sopenharmony_ci return -ENOMEM; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci /* register as a fcoe transport */ 24808c2ecf20Sopenharmony_ci rc = fcoe_transport_attach(&fcoe_sw_transport); 24818c2ecf20Sopenharmony_ci if (rc) { 24828c2ecf20Sopenharmony_ci printk(KERN_ERR "failed to register an fcoe transport, check " 24838c2ecf20Sopenharmony_ci "if libfcoe is loaded\n"); 24848c2ecf20Sopenharmony_ci goto out_destroy; 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 24908c2ecf20Sopenharmony_ci p = per_cpu_ptr(&fcoe_percpu, cpu); 24918c2ecf20Sopenharmony_ci INIT_WORK(&p->work, fcoe_receive_work); 24928c2ecf20Sopenharmony_ci skb_queue_head_init(&p->fcoe_rx_list); 24938c2ecf20Sopenharmony_ci } 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci /* Setup link change notification */ 24968c2ecf20Sopenharmony_ci fcoe_dev_setup(); 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci rc = fcoe_if_init(); 24998c2ecf20Sopenharmony_ci if (rc) 25008c2ecf20Sopenharmony_ci goto out_free; 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 25038c2ecf20Sopenharmony_ci return 0; 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ciout_free: 25068c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 25078c2ecf20Sopenharmony_ci fcoe_transport_detach(&fcoe_sw_transport); 25088c2ecf20Sopenharmony_ciout_destroy: 25098c2ecf20Sopenharmony_ci destroy_workqueue(fcoe_wq); 25108c2ecf20Sopenharmony_ci return rc; 25118c2ecf20Sopenharmony_ci} 25128c2ecf20Sopenharmony_cimodule_init(fcoe_init); 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci/** 25158c2ecf20Sopenharmony_ci * fcoe_exit() - Clean up fcoe.ko 25168c2ecf20Sopenharmony_ci * 25178c2ecf20Sopenharmony_ci * Returns: 0 on success or a negative value on failure 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_cistatic void __exit fcoe_exit(void) 25208c2ecf20Sopenharmony_ci{ 25218c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe, *tmp; 25228c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr; 25238c2ecf20Sopenharmony_ci struct fcoe_port *port; 25248c2ecf20Sopenharmony_ci unsigned int cpu; 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci fcoe_dev_cleanup(); 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci /* releases the associated fcoe hosts */ 25318c2ecf20Sopenharmony_ci rtnl_lock(); 25328c2ecf20Sopenharmony_ci list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { 25338c2ecf20Sopenharmony_ci ctlr = fcoe_to_ctlr(fcoe); 25348c2ecf20Sopenharmony_ci port = lport_priv(ctlr->lp); 25358c2ecf20Sopenharmony_ci fcoe_hostlist_del(port->lport); 25368c2ecf20Sopenharmony_ci queue_work(fcoe_wq, &port->destroy_work); 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci rtnl_unlock(); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) 25418c2ecf20Sopenharmony_ci fcoe_thread_cleanup_local(cpu); 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci /* 25468c2ecf20Sopenharmony_ci * destroy_work's may be chained but destroy_workqueue() 25478c2ecf20Sopenharmony_ci * can take care of them. Just kill the fcoe_wq. 25488c2ecf20Sopenharmony_ci */ 25498c2ecf20Sopenharmony_ci destroy_workqueue(fcoe_wq); 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci /* 25528c2ecf20Sopenharmony_ci * Detaching from the scsi transport must happen after all 25538c2ecf20Sopenharmony_ci * destroys are done on the fcoe_wq. destroy_workqueue will 25548c2ecf20Sopenharmony_ci * enusre the fcoe_wq is flushed. 25558c2ecf20Sopenharmony_ci */ 25568c2ecf20Sopenharmony_ci fcoe_if_exit(); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* detach from fcoe transport */ 25598c2ecf20Sopenharmony_ci fcoe_transport_detach(&fcoe_sw_transport); 25608c2ecf20Sopenharmony_ci} 25618c2ecf20Sopenharmony_cimodule_exit(fcoe_exit); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci/** 25648c2ecf20Sopenharmony_ci * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler 25658c2ecf20Sopenharmony_ci * @seq: active sequence in the FLOGI or FDISC exchange 25668c2ecf20Sopenharmony_ci * @fp: response frame, or error encoded in a pointer (timeout) 25678c2ecf20Sopenharmony_ci * @arg: pointer to the fcoe_ctlr structure 25688c2ecf20Sopenharmony_ci * 25698c2ecf20Sopenharmony_ci * This handles MAC address management for FCoE, then passes control on to 25708c2ecf20Sopenharmony_ci * the libfc FLOGI response handler. 25718c2ecf20Sopenharmony_ci */ 25728c2ecf20Sopenharmony_cistatic void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) 25738c2ecf20Sopenharmony_ci{ 25748c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = arg; 25758c2ecf20Sopenharmony_ci struct fc_exch *exch = fc_seq_exch(seq); 25768c2ecf20Sopenharmony_ci struct fc_lport *lport = exch->lp; 25778c2ecf20Sopenharmony_ci u8 *mac; 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci if (IS_ERR(fp)) 25808c2ecf20Sopenharmony_ci goto done; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci mac = fr_cb(fp)->granted_mac; 25838c2ecf20Sopenharmony_ci /* pre-FIP */ 25848c2ecf20Sopenharmony_ci if (is_zero_ether_addr(mac)) 25858c2ecf20Sopenharmony_ci fcoe_ctlr_recv_flogi(fip, lport, fp); 25868c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(mac)) 25878c2ecf20Sopenharmony_ci fcoe_update_src_mac(lport, mac); 25888c2ecf20Sopenharmony_cidone: 25898c2ecf20Sopenharmony_ci fc_lport_flogi_resp(seq, fp, lport); 25908c2ecf20Sopenharmony_ci} 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci/** 25938c2ecf20Sopenharmony_ci * fcoe_logo_resp() - FCoE specific LOGO response handler 25948c2ecf20Sopenharmony_ci * @seq: active sequence in the LOGO exchange 25958c2ecf20Sopenharmony_ci * @fp: response frame, or error encoded in a pointer (timeout) 25968c2ecf20Sopenharmony_ci * @arg: pointer to the fcoe_ctlr structure 25978c2ecf20Sopenharmony_ci * 25988c2ecf20Sopenharmony_ci * This handles MAC address management for FCoE, then passes control on to 25998c2ecf20Sopenharmony_ci * the libfc LOGO response handler. 26008c2ecf20Sopenharmony_ci */ 26018c2ecf20Sopenharmony_cistatic void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) 26028c2ecf20Sopenharmony_ci{ 26038c2ecf20Sopenharmony_ci struct fc_lport *lport = arg; 26048c2ecf20Sopenharmony_ci static u8 zero_mac[ETH_ALEN] = { 0 }; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci if (!IS_ERR(fp)) 26078c2ecf20Sopenharmony_ci fcoe_update_src_mac(lport, zero_mac); 26088c2ecf20Sopenharmony_ci fc_lport_logo_resp(seq, fp, lport); 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci/* 26128c2ecf20Sopenharmony_ci * fcoe_elsct_send - FCoE specific ELS handler 26138c2ecf20Sopenharmony_ci * 26148c2ecf20Sopenharmony_ci * This does special case handling of FIP encapsualted ELS exchanges for FCoE, 26158c2ecf20Sopenharmony_ci * using FCoE specific response handlers and passing the FIP controller as 26168c2ecf20Sopenharmony_ci * the argument (the lport is still available from the exchange). 26178c2ecf20Sopenharmony_ci * 26188c2ecf20Sopenharmony_ci * Most of the work here is just handed off to the libfc routine. 26198c2ecf20Sopenharmony_ci */ 26208c2ecf20Sopenharmony_cistatic struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, 26218c2ecf20Sopenharmony_ci struct fc_frame *fp, unsigned int op, 26228c2ecf20Sopenharmony_ci void (*resp)(struct fc_seq *, 26238c2ecf20Sopenharmony_ci struct fc_frame *, 26248c2ecf20Sopenharmony_ci void *), 26258c2ecf20Sopenharmony_ci void *arg, u32 timeout) 26268c2ecf20Sopenharmony_ci{ 26278c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 26288c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 26298c2ecf20Sopenharmony_ci struct fcoe_ctlr *fip = fcoe_to_ctlr(fcoe); 26308c2ecf20Sopenharmony_ci struct fc_frame_header *fh = fc_frame_header_get(fp); 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci switch (op) { 26338c2ecf20Sopenharmony_ci case ELS_FLOGI: 26348c2ecf20Sopenharmony_ci case ELS_FDISC: 26358c2ecf20Sopenharmony_ci if (lport->point_to_multipoint) 26368c2ecf20Sopenharmony_ci break; 26378c2ecf20Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp, 26388c2ecf20Sopenharmony_ci fip, timeout); 26398c2ecf20Sopenharmony_ci case ELS_LOGO: 26408c2ecf20Sopenharmony_ci /* only hook onto fabric logouts, not port logouts */ 26418c2ecf20Sopenharmony_ci if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) 26428c2ecf20Sopenharmony_ci break; 26438c2ecf20Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp, 26448c2ecf20Sopenharmony_ci lport, timeout); 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); 26478c2ecf20Sopenharmony_ci} 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci/** 26508c2ecf20Sopenharmony_ci * fcoe_vport_create() - create an fc_host/scsi_host for a vport 26518c2ecf20Sopenharmony_ci * @vport: fc_vport object to create a new fc_host for 26528c2ecf20Sopenharmony_ci * @disabled: start the new fc_host in a disabled state by default? 26538c2ecf20Sopenharmony_ci * 26548c2ecf20Sopenharmony_ci * Returns: 0 for success 26558c2ecf20Sopenharmony_ci */ 26568c2ecf20Sopenharmony_cistatic int fcoe_vport_create(struct fc_vport *vport, bool disabled) 26578c2ecf20Sopenharmony_ci{ 26588c2ecf20Sopenharmony_ci struct Scsi_Host *shost = vport_to_shost(vport); 26598c2ecf20Sopenharmony_ci struct fc_lport *n_port = shost_priv(shost); 26608c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(n_port); 26618c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 26628c2ecf20Sopenharmony_ci struct net_device *netdev = fcoe->netdev; 26638c2ecf20Sopenharmony_ci struct fc_lport *vn_port; 26648c2ecf20Sopenharmony_ci int rc; 26658c2ecf20Sopenharmony_ci char buf[32]; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci rc = fcoe_validate_vport_create(vport); 26688c2ecf20Sopenharmony_ci if (rc) { 26698c2ecf20Sopenharmony_ci fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf)); 26708c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe: Failed to create vport, " 26718c2ecf20Sopenharmony_ci "WWPN (0x%s) already exists\n", 26728c2ecf20Sopenharmony_ci buf); 26738c2ecf20Sopenharmony_ci return rc; 26748c2ecf20Sopenharmony_ci } 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 26778c2ecf20Sopenharmony_ci rtnl_lock(); 26788c2ecf20Sopenharmony_ci vn_port = fcoe_if_create(fcoe, &vport->dev, 1); 26798c2ecf20Sopenharmony_ci rtnl_unlock(); 26808c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci if (IS_ERR(vn_port)) { 26838c2ecf20Sopenharmony_ci printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n", 26848c2ecf20Sopenharmony_ci netdev->name); 26858c2ecf20Sopenharmony_ci return -EIO; 26868c2ecf20Sopenharmony_ci } 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci if (disabled) { 26898c2ecf20Sopenharmony_ci fc_vport_set_state(vport, FC_VPORT_DISABLED); 26908c2ecf20Sopenharmony_ci } else { 26918c2ecf20Sopenharmony_ci vn_port->boot_time = jiffies; 26928c2ecf20Sopenharmony_ci fc_fabric_login(vn_port); 26938c2ecf20Sopenharmony_ci fc_vport_setlink(vn_port); 26948c2ecf20Sopenharmony_ci } 26958c2ecf20Sopenharmony_ci return 0; 26968c2ecf20Sopenharmony_ci} 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci/** 26998c2ecf20Sopenharmony_ci * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport 27008c2ecf20Sopenharmony_ci * @vport: fc_vport object that is being destroyed 27018c2ecf20Sopenharmony_ci * 27028c2ecf20Sopenharmony_ci * Returns: 0 for success 27038c2ecf20Sopenharmony_ci */ 27048c2ecf20Sopenharmony_cistatic int fcoe_vport_destroy(struct fc_vport *vport) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct Scsi_Host *shost = vport_to_shost(vport); 27078c2ecf20Sopenharmony_ci struct fc_lport *n_port = shost_priv(shost); 27088c2ecf20Sopenharmony_ci struct fc_lport *vn_port = vport->dd_data; 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci mutex_lock(&n_port->lp_mutex); 27118c2ecf20Sopenharmony_ci list_del(&vn_port->list); 27128c2ecf20Sopenharmony_ci mutex_unlock(&n_port->lp_mutex); 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci mutex_lock(&fcoe_config_mutex); 27158c2ecf20Sopenharmony_ci rtnl_lock(); 27168c2ecf20Sopenharmony_ci fcoe_if_destroy(vn_port); 27178c2ecf20Sopenharmony_ci rtnl_unlock(); 27188c2ecf20Sopenharmony_ci mutex_unlock(&fcoe_config_mutex); 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci return 0; 27218c2ecf20Sopenharmony_ci} 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci/** 27248c2ecf20Sopenharmony_ci * fcoe_vport_remove() - remove attached vports 27258c2ecf20Sopenharmony_ci * @lport: lport for which the vports should be removed 27268c2ecf20Sopenharmony_ci */ 27278c2ecf20Sopenharmony_cistatic void fcoe_vport_remove(struct fc_lport *lport) 27288c2ecf20Sopenharmony_ci{ 27298c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 27308c2ecf20Sopenharmony_ci struct fc_host_attrs *fc_host; 27318c2ecf20Sopenharmony_ci unsigned long flags; 27328c2ecf20Sopenharmony_ci struct fc_vport *vport; 27338c2ecf20Sopenharmony_ci struct fc_vport *next_vport; 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci shost = lport->host; 27368c2ecf20Sopenharmony_ci fc_host = shost_to_fc_host(shost); 27378c2ecf20Sopenharmony_ci 27388c2ecf20Sopenharmony_ci /* Loop through all the vports and mark them for deletion */ 27398c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 27408c2ecf20Sopenharmony_ci list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) { 27418c2ecf20Sopenharmony_ci if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) { 27428c2ecf20Sopenharmony_ci continue; 27438c2ecf20Sopenharmony_ci } else { 27448c2ecf20Sopenharmony_ci vport->flags |= FC_VPORT_DELETING; 27458c2ecf20Sopenharmony_ci queue_work(fc_host_work_q(shost), 27468c2ecf20Sopenharmony_ci &vport->vport_delete_work); 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci } 27498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci flush_workqueue(fc_host_work_q(shost)); 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci/** 27558c2ecf20Sopenharmony_ci * fcoe_vport_disable() - change vport state 27568c2ecf20Sopenharmony_ci * @vport: vport to bring online/offline 27578c2ecf20Sopenharmony_ci * @disable: should the vport be disabled? 27588c2ecf20Sopenharmony_ci */ 27598c2ecf20Sopenharmony_cistatic int fcoe_vport_disable(struct fc_vport *vport, bool disable) 27608c2ecf20Sopenharmony_ci{ 27618c2ecf20Sopenharmony_ci struct fc_lport *lport = vport->dd_data; 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci if (disable) { 27648c2ecf20Sopenharmony_ci fc_vport_set_state(vport, FC_VPORT_DISABLED); 27658c2ecf20Sopenharmony_ci fc_fabric_logoff(lport); 27668c2ecf20Sopenharmony_ci } else { 27678c2ecf20Sopenharmony_ci lport->boot_time = jiffies; 27688c2ecf20Sopenharmony_ci fc_fabric_login(lport); 27698c2ecf20Sopenharmony_ci fc_vport_setlink(lport); 27708c2ecf20Sopenharmony_ci } 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci return 0; 27738c2ecf20Sopenharmony_ci} 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci/** 27768c2ecf20Sopenharmony_ci * fcoe_vport_set_symbolic_name() - append vport string to symbolic name 27778c2ecf20Sopenharmony_ci * @vport: fc_vport with a new symbolic name string 27788c2ecf20Sopenharmony_ci * 27798c2ecf20Sopenharmony_ci * After generating a new symbolic name string, a new RSPN_ID request is 27808c2ecf20Sopenharmony_ci * sent to the name server. There is no response handler, so if it fails 27818c2ecf20Sopenharmony_ci * for some reason it will not be retried. 27828c2ecf20Sopenharmony_ci */ 27838c2ecf20Sopenharmony_cistatic void fcoe_set_vport_symbolic_name(struct fc_vport *vport) 27848c2ecf20Sopenharmony_ci{ 27858c2ecf20Sopenharmony_ci struct fc_lport *lport = vport->dd_data; 27868c2ecf20Sopenharmony_ci struct fc_frame *fp; 27878c2ecf20Sopenharmony_ci size_t len; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, 27908c2ecf20Sopenharmony_ci "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION, 27918c2ecf20Sopenharmony_ci fcoe_netdev(lport)->name, vport->symbolic_name); 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci if (lport->state != LPORT_ST_READY) 27948c2ecf20Sopenharmony_ci return; 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci len = strnlen(fc_host_symbolic_name(lport->host), 255); 27978c2ecf20Sopenharmony_ci fp = fc_frame_alloc(lport, 27988c2ecf20Sopenharmony_ci sizeof(struct fc_ct_hdr) + 27998c2ecf20Sopenharmony_ci sizeof(struct fc_ns_rspn) + len); 28008c2ecf20Sopenharmony_ci if (!fp) 28018c2ecf20Sopenharmony_ci return; 28028c2ecf20Sopenharmony_ci lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID, 28038c2ecf20Sopenharmony_ci NULL, NULL, 3 * lport->r_a_tov); 28048c2ecf20Sopenharmony_ci} 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_cistatic void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) 28078c2ecf20Sopenharmony_ci{ 28088c2ecf20Sopenharmony_ci struct fcoe_ctlr_device *ctlr_dev = 28098c2ecf20Sopenharmony_ci fcoe_fcf_dev_to_ctlr_dev(fcf_dev); 28108c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); 28118c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = fcoe_ctlr_priv(ctlr); 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci fcf_dev->vlan_id = vlan_dev_vlan_id(fcoe->netdev); 28148c2ecf20Sopenharmony_ci} 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci/** 28178c2ecf20Sopenharmony_ci * fcoe_set_port_id() - Callback from libfc when Port_ID is set. 28188c2ecf20Sopenharmony_ci * @lport: the local port 28198c2ecf20Sopenharmony_ci * @port_id: the port ID 28208c2ecf20Sopenharmony_ci * @fp: the received frame, if any, that caused the port_id to be set. 28218c2ecf20Sopenharmony_ci * 28228c2ecf20Sopenharmony_ci * This routine handles the case where we received a FLOGI and are 28238c2ecf20Sopenharmony_ci * entering point-to-point mode. We need to call fcoe_ctlr_recv_flogi() 28248c2ecf20Sopenharmony_ci * so it can set the non-mapped mode and gateway address. 28258c2ecf20Sopenharmony_ci * 28268c2ecf20Sopenharmony_ci * The FLOGI LS_ACC is handled by fcoe_flogi_resp(). 28278c2ecf20Sopenharmony_ci */ 28288c2ecf20Sopenharmony_cistatic void fcoe_set_port_id(struct fc_lport *lport, 28298c2ecf20Sopenharmony_ci u32 port_id, struct fc_frame *fp) 28308c2ecf20Sopenharmony_ci{ 28318c2ecf20Sopenharmony_ci struct fcoe_port *port = lport_priv(lport); 28328c2ecf20Sopenharmony_ci struct fcoe_interface *fcoe = port->priv; 28338c2ecf20Sopenharmony_ci struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) 28368c2ecf20Sopenharmony_ci fcoe_ctlr_recv_flogi(ctlr, lport, fp); 28378c2ecf20Sopenharmony_ci} 2838