162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* Authors: Bernard Metzler <bmt@zurich.ibm.com> */ 462306a36Sopenharmony_ci/* Copyright (c) 2008-2019, IBM Corporation */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci#include <linux/inetdevice.h> 1062306a36Sopenharmony_ci#include <net/net_namespace.h> 1162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1262306a36Sopenharmony_ci#include <linux/if_arp.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <net/addrconf.h> 2062306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 2162306a36Sopenharmony_ci#include <rdma/ib_user_verbs.h> 2262306a36Sopenharmony_ci#include <rdma/rdma_netlink.h> 2362306a36Sopenharmony_ci#include <linux/kthread.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "siw.h" 2662306a36Sopenharmony_ci#include "siw_verbs.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciMODULE_AUTHOR("Bernard Metzler"); 2962306a36Sopenharmony_ciMODULE_DESCRIPTION("Software iWARP Driver"); 3062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* transmit from user buffer, if possible */ 3362306a36Sopenharmony_ciconst bool zcopy_tx = true; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Restrict usage of GSO, if hardware peer iwarp is unable to process 3662306a36Sopenharmony_ci * large packets. try_gso = true lets siw try to use local GSO, 3762306a36Sopenharmony_ci * if peer agrees. Not using GSO severly limits siw maximum tx bandwidth. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ciconst bool try_gso; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Attach siw also with loopback devices */ 4262306a36Sopenharmony_ciconst bool loopback_enabled = true; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* We try to negotiate CRC on, if true */ 4562306a36Sopenharmony_ciconst bool mpa_crc_required; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* MPA CRC on/off enforced */ 4862306a36Sopenharmony_ciconst bool mpa_crc_strict; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Control TCP_NODELAY socket option */ 5162306a36Sopenharmony_ciconst bool siw_tcp_nagle; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Select MPA version to be used during connection setup */ 5462306a36Sopenharmony_ciu_char mpa_version = MPA_REVISION_2; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Selects MPA P2P mode (additional handshake during connection 5762306a36Sopenharmony_ci * setup, if true. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ciconst bool peer_to_peer; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct task_struct *siw_tx_thread[NR_CPUS]; 6262306a36Sopenharmony_cistruct crypto_shash *siw_crypto_shash; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int siw_device_register(struct siw_device *sdev, const char *name) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct ib_device *base_dev = &sdev->base_dev; 6762306a36Sopenharmony_ci static int dev_id = 1; 6862306a36Sopenharmony_ci int rv; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci sdev->vendor_part_id = dev_id++; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci rv = ib_register_device(base_dev, name, NULL); 7362306a36Sopenharmony_ci if (rv) { 7462306a36Sopenharmony_ci pr_warn("siw: device registration error %d\n", rv); 7562306a36Sopenharmony_ci return rv; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci siw_dbg(base_dev, "HWaddr=%pM\n", sdev->raw_gid); 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void siw_device_cleanup(struct ib_device *base_dev) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct siw_device *sdev = to_siw_dev(base_dev); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci xa_destroy(&sdev->qp_xa); 8762306a36Sopenharmony_ci xa_destroy(&sdev->mem_xa); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int siw_dev_qualified(struct net_device *netdev) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * Additional hardware support can be added here 9462306a36Sopenharmony_ci * (e.g. ARPHRD_FDDI, ARPHRD_ATM, ...) - see 9562306a36Sopenharmony_ci * <linux/if_arp.h> for type identifiers. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci if (netdev->type == ARPHRD_ETHER || netdev->type == ARPHRD_IEEE802 || 9862306a36Sopenharmony_ci netdev->type == ARPHRD_NONE || 9962306a36Sopenharmony_ci (netdev->type == ARPHRD_LOOPBACK && loopback_enabled)) 10062306a36Sopenharmony_ci return 1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic DEFINE_PER_CPU(atomic_t, siw_use_cnt); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic struct { 10862306a36Sopenharmony_ci struct cpumask **tx_valid_cpus; 10962306a36Sopenharmony_ci int num_nodes; 11062306a36Sopenharmony_ci} siw_cpu_info; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int siw_init_cpulist(void) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci int i, num_nodes = nr_node_ids; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci memset(siw_tx_thread, 0, sizeof(siw_tx_thread)); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci siw_cpu_info.num_nodes = num_nodes; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci siw_cpu_info.tx_valid_cpus = 12162306a36Sopenharmony_ci kcalloc(num_nodes, sizeof(struct cpumask *), GFP_KERNEL); 12262306a36Sopenharmony_ci if (!siw_cpu_info.tx_valid_cpus) { 12362306a36Sopenharmony_ci siw_cpu_info.num_nodes = 0; 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci for (i = 0; i < siw_cpu_info.num_nodes; i++) { 12762306a36Sopenharmony_ci siw_cpu_info.tx_valid_cpus[i] = 12862306a36Sopenharmony_ci kzalloc(sizeof(struct cpumask), GFP_KERNEL); 12962306a36Sopenharmony_ci if (!siw_cpu_info.tx_valid_cpus[i]) 13062306a36Sopenharmony_ci goto out_err; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci cpumask_clear(siw_cpu_info.tx_valid_cpus[i]); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci for_each_possible_cpu(i) 13562306a36Sopenharmony_ci cpumask_set_cpu(i, siw_cpu_info.tx_valid_cpus[cpu_to_node(i)]); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciout_err: 14062306a36Sopenharmony_ci siw_cpu_info.num_nodes = 0; 14162306a36Sopenharmony_ci while (--i >= 0) 14262306a36Sopenharmony_ci kfree(siw_cpu_info.tx_valid_cpus[i]); 14362306a36Sopenharmony_ci kfree(siw_cpu_info.tx_valid_cpus); 14462306a36Sopenharmony_ci siw_cpu_info.tx_valid_cpus = NULL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return -ENOMEM; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void siw_destroy_cpulist(void) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int i = 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci while (i < siw_cpu_info.num_nodes) 15462306a36Sopenharmony_ci kfree(siw_cpu_info.tx_valid_cpus[i++]); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci kfree(siw_cpu_info.tx_valid_cpus); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Choose CPU with least number of active QP's from NUMA node of 16162306a36Sopenharmony_ci * TX interface. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ciint siw_get_tx_cpu(struct siw_device *sdev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci const struct cpumask *tx_cpumask; 16662306a36Sopenharmony_ci int i, num_cpus, cpu, min_use, node = sdev->numa_node, tx_cpu = -1; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (node < 0) 16962306a36Sopenharmony_ci tx_cpumask = cpu_online_mask; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci tx_cpumask = siw_cpu_info.tx_valid_cpus[node]; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci num_cpus = cpumask_weight(tx_cpumask); 17462306a36Sopenharmony_ci if (!num_cpus) { 17562306a36Sopenharmony_ci /* no CPU on this NUMA node */ 17662306a36Sopenharmony_ci tx_cpumask = cpu_online_mask; 17762306a36Sopenharmony_ci num_cpus = cpumask_weight(tx_cpumask); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci if (!num_cpus) 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci cpu = cpumask_first(tx_cpumask); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (i = 0, min_use = SIW_MAX_QP; i < num_cpus; 18562306a36Sopenharmony_ci i++, cpu = cpumask_next(cpu, tx_cpumask)) { 18662306a36Sopenharmony_ci int usage; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Skip any cores which have no TX thread */ 18962306a36Sopenharmony_ci if (!siw_tx_thread[cpu]) 19062306a36Sopenharmony_ci continue; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci usage = atomic_read(&per_cpu(siw_use_cnt, cpu)); 19362306a36Sopenharmony_ci if (usage <= min_use) { 19462306a36Sopenharmony_ci tx_cpu = cpu; 19562306a36Sopenharmony_ci min_use = usage; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci siw_dbg(&sdev->base_dev, 19962306a36Sopenharmony_ci "tx cpu %d, node %d, %d qp's\n", tx_cpu, node, min_use); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciout: 20262306a36Sopenharmony_ci if (tx_cpu >= 0) 20362306a36Sopenharmony_ci atomic_inc(&per_cpu(siw_use_cnt, tx_cpu)); 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci pr_warn("siw: no tx cpu found\n"); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return tx_cpu; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_civoid siw_put_tx_cpu(int cpu) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci atomic_dec(&per_cpu(siw_use_cnt, cpu)); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic struct ib_qp *siw_get_base_qp(struct ib_device *base_dev, int id) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct siw_qp *qp = siw_qp_id2obj(to_siw_dev(base_dev), id); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (qp) { 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * siw_qp_id2obj() increments object reference count 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci siw_qp_put(qp); 22462306a36Sopenharmony_ci return &qp->base_qp; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci return NULL; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic const struct ib_device_ops siw_device_ops = { 23062306a36Sopenharmony_ci .owner = THIS_MODULE, 23162306a36Sopenharmony_ci .uverbs_abi_ver = SIW_ABI_VERSION, 23262306a36Sopenharmony_ci .driver_id = RDMA_DRIVER_SIW, 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci .alloc_mr = siw_alloc_mr, 23562306a36Sopenharmony_ci .alloc_pd = siw_alloc_pd, 23662306a36Sopenharmony_ci .alloc_ucontext = siw_alloc_ucontext, 23762306a36Sopenharmony_ci .create_cq = siw_create_cq, 23862306a36Sopenharmony_ci .create_qp = siw_create_qp, 23962306a36Sopenharmony_ci .create_srq = siw_create_srq, 24062306a36Sopenharmony_ci .dealloc_driver = siw_device_cleanup, 24162306a36Sopenharmony_ci .dealloc_pd = siw_dealloc_pd, 24262306a36Sopenharmony_ci .dealloc_ucontext = siw_dealloc_ucontext, 24362306a36Sopenharmony_ci .dereg_mr = siw_dereg_mr, 24462306a36Sopenharmony_ci .destroy_cq = siw_destroy_cq, 24562306a36Sopenharmony_ci .destroy_qp = siw_destroy_qp, 24662306a36Sopenharmony_ci .destroy_srq = siw_destroy_srq, 24762306a36Sopenharmony_ci .get_dma_mr = siw_get_dma_mr, 24862306a36Sopenharmony_ci .get_port_immutable = siw_get_port_immutable, 24962306a36Sopenharmony_ci .iw_accept = siw_accept, 25062306a36Sopenharmony_ci .iw_add_ref = siw_qp_get_ref, 25162306a36Sopenharmony_ci .iw_connect = siw_connect, 25262306a36Sopenharmony_ci .iw_create_listen = siw_create_listen, 25362306a36Sopenharmony_ci .iw_destroy_listen = siw_destroy_listen, 25462306a36Sopenharmony_ci .iw_get_qp = siw_get_base_qp, 25562306a36Sopenharmony_ci .iw_reject = siw_reject, 25662306a36Sopenharmony_ci .iw_rem_ref = siw_qp_put_ref, 25762306a36Sopenharmony_ci .map_mr_sg = siw_map_mr_sg, 25862306a36Sopenharmony_ci .mmap = siw_mmap, 25962306a36Sopenharmony_ci .mmap_free = siw_mmap_free, 26062306a36Sopenharmony_ci .modify_qp = siw_verbs_modify_qp, 26162306a36Sopenharmony_ci .modify_srq = siw_modify_srq, 26262306a36Sopenharmony_ci .poll_cq = siw_poll_cq, 26362306a36Sopenharmony_ci .post_recv = siw_post_receive, 26462306a36Sopenharmony_ci .post_send = siw_post_send, 26562306a36Sopenharmony_ci .post_srq_recv = siw_post_srq_recv, 26662306a36Sopenharmony_ci .query_device = siw_query_device, 26762306a36Sopenharmony_ci .query_gid = siw_query_gid, 26862306a36Sopenharmony_ci .query_port = siw_query_port, 26962306a36Sopenharmony_ci .query_qp = siw_query_qp, 27062306a36Sopenharmony_ci .query_srq = siw_query_srq, 27162306a36Sopenharmony_ci .req_notify_cq = siw_req_notify_cq, 27262306a36Sopenharmony_ci .reg_user_mr = siw_reg_user_mr, 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci INIT_RDMA_OBJ_SIZE(ib_cq, siw_cq, base_cq), 27562306a36Sopenharmony_ci INIT_RDMA_OBJ_SIZE(ib_pd, siw_pd, base_pd), 27662306a36Sopenharmony_ci INIT_RDMA_OBJ_SIZE(ib_qp, siw_qp, base_qp), 27762306a36Sopenharmony_ci INIT_RDMA_OBJ_SIZE(ib_srq, siw_srq, base_srq), 27862306a36Sopenharmony_ci INIT_RDMA_OBJ_SIZE(ib_ucontext, siw_ucontext, base_ucontext), 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic struct siw_device *siw_device_create(struct net_device *netdev) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct siw_device *sdev = NULL; 28462306a36Sopenharmony_ci struct ib_device *base_dev; 28562306a36Sopenharmony_ci int rv; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci sdev = ib_alloc_device(siw_device, base_dev); 28862306a36Sopenharmony_ci if (!sdev) 28962306a36Sopenharmony_ci return NULL; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci base_dev = &sdev->base_dev; 29262306a36Sopenharmony_ci sdev->netdev = netdev; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (netdev->addr_len) { 29562306a36Sopenharmony_ci memcpy(sdev->raw_gid, netdev->dev_addr, 29662306a36Sopenharmony_ci min_t(unsigned int, netdev->addr_len, ETH_ALEN)); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * This device does not have a HW address, but 30062306a36Sopenharmony_ci * connection mangagement requires a unique gid. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci eth_random_addr(sdev->raw_gid); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci addrconf_addr_eui48((u8 *)&base_dev->node_guid, sdev->raw_gid); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci base_dev->uverbs_cmd_mask |= BIT_ULL(IB_USER_VERBS_CMD_POST_SEND); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci base_dev->node_type = RDMA_NODE_RNIC; 30962306a36Sopenharmony_ci memcpy(base_dev->node_desc, SIW_NODE_DESC_COMMON, 31062306a36Sopenharmony_ci sizeof(SIW_NODE_DESC_COMMON)); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Current model (one-to-one device association): 31462306a36Sopenharmony_ci * One Softiwarp device per net_device or, equivalently, 31562306a36Sopenharmony_ci * per physical port. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci base_dev->phys_port_cnt = 1; 31862306a36Sopenharmony_ci base_dev->num_comp_vectors = num_possible_cpus(); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci xa_init_flags(&sdev->qp_xa, XA_FLAGS_ALLOC1); 32162306a36Sopenharmony_ci xa_init_flags(&sdev->mem_xa, XA_FLAGS_ALLOC1); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ib_set_device_ops(base_dev, &siw_device_ops); 32462306a36Sopenharmony_ci rv = ib_device_set_netdev(base_dev, netdev, 1); 32562306a36Sopenharmony_ci if (rv) 32662306a36Sopenharmony_ci goto error; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci memcpy(base_dev->iw_ifname, netdev->name, 32962306a36Sopenharmony_ci sizeof(base_dev->iw_ifname)); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Disable TCP port mapping */ 33262306a36Sopenharmony_ci base_dev->iw_driver_flags = IW_F_NO_PORT_MAP; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci sdev->attrs.max_qp = SIW_MAX_QP; 33562306a36Sopenharmony_ci sdev->attrs.max_qp_wr = SIW_MAX_QP_WR; 33662306a36Sopenharmony_ci sdev->attrs.max_ord = SIW_MAX_ORD_QP; 33762306a36Sopenharmony_ci sdev->attrs.max_ird = SIW_MAX_IRD_QP; 33862306a36Sopenharmony_ci sdev->attrs.max_sge = SIW_MAX_SGE; 33962306a36Sopenharmony_ci sdev->attrs.max_sge_rd = SIW_MAX_SGE_RD; 34062306a36Sopenharmony_ci sdev->attrs.max_cq = SIW_MAX_CQ; 34162306a36Sopenharmony_ci sdev->attrs.max_cqe = SIW_MAX_CQE; 34262306a36Sopenharmony_ci sdev->attrs.max_mr = SIW_MAX_MR; 34362306a36Sopenharmony_ci sdev->attrs.max_pd = SIW_MAX_PD; 34462306a36Sopenharmony_ci sdev->attrs.max_mw = SIW_MAX_MW; 34562306a36Sopenharmony_ci sdev->attrs.max_srq = SIW_MAX_SRQ; 34662306a36Sopenharmony_ci sdev->attrs.max_srq_wr = SIW_MAX_SRQ_WR; 34762306a36Sopenharmony_ci sdev->attrs.max_srq_sge = SIW_MAX_SGE; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->cep_list); 35062306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->qp_list); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci atomic_set(&sdev->num_ctx, 0); 35362306a36Sopenharmony_ci atomic_set(&sdev->num_srq, 0); 35462306a36Sopenharmony_ci atomic_set(&sdev->num_qp, 0); 35562306a36Sopenharmony_ci atomic_set(&sdev->num_cq, 0); 35662306a36Sopenharmony_ci atomic_set(&sdev->num_mr, 0); 35762306a36Sopenharmony_ci atomic_set(&sdev->num_pd, 0); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci sdev->numa_node = dev_to_node(&netdev->dev); 36062306a36Sopenharmony_ci spin_lock_init(&sdev->lock); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return sdev; 36362306a36Sopenharmony_cierror: 36462306a36Sopenharmony_ci ib_dealloc_device(base_dev); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return NULL; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* 37062306a36Sopenharmony_ci * Network link becomes unavailable. Mark all 37162306a36Sopenharmony_ci * affected QP's accordingly. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_cistatic void siw_netdev_down(struct work_struct *work) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct siw_device *sdev = 37662306a36Sopenharmony_ci container_of(work, struct siw_device, netdev_down); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci struct siw_qp_attrs qp_attrs; 37962306a36Sopenharmony_ci struct list_head *pos, *tmp; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci memset(&qp_attrs, 0, sizeof(qp_attrs)); 38262306a36Sopenharmony_ci qp_attrs.state = SIW_QP_STATE_ERROR; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci list_for_each_safe(pos, tmp, &sdev->qp_list) { 38562306a36Sopenharmony_ci struct siw_qp *qp = list_entry(pos, struct siw_qp, devq); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci down_write(&qp->state_lock); 38862306a36Sopenharmony_ci WARN_ON(siw_qp_modify(qp, &qp_attrs, SIW_QP_ATTR_STATE)); 38962306a36Sopenharmony_ci up_write(&qp->state_lock); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci ib_device_put(&sdev->base_dev); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void siw_device_goes_down(struct siw_device *sdev) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci if (ib_device_try_get(&sdev->base_dev)) { 39762306a36Sopenharmony_ci INIT_WORK(&sdev->netdev_down, siw_netdev_down); 39862306a36Sopenharmony_ci schedule_work(&sdev->netdev_down); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int siw_netdev_event(struct notifier_block *nb, unsigned long event, 40362306a36Sopenharmony_ci void *arg) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct net_device *netdev = netdev_notifier_info_to_dev(arg); 40662306a36Sopenharmony_ci struct ib_device *base_dev; 40762306a36Sopenharmony_ci struct siw_device *sdev; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci dev_dbg(&netdev->dev, "siw: event %lu\n", event); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci base_dev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_SIW); 41262306a36Sopenharmony_ci if (!base_dev) 41362306a36Sopenharmony_ci return NOTIFY_OK; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci sdev = to_siw_dev(base_dev); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci switch (event) { 41862306a36Sopenharmony_ci case NETDEV_UP: 41962306a36Sopenharmony_ci sdev->state = IB_PORT_ACTIVE; 42062306a36Sopenharmony_ci siw_port_event(sdev, 1, IB_EVENT_PORT_ACTIVE); 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci case NETDEV_GOING_DOWN: 42462306a36Sopenharmony_ci siw_device_goes_down(sdev); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci case NETDEV_DOWN: 42862306a36Sopenharmony_ci sdev->state = IB_PORT_DOWN; 42962306a36Sopenharmony_ci siw_port_event(sdev, 1, IB_EVENT_PORT_ERR); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci case NETDEV_REGISTER: 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Device registration now handled only by 43562306a36Sopenharmony_ci * rdma netlink commands. So it shall be impossible 43662306a36Sopenharmony_ci * to end up here with a valid siw device. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci siw_dbg(base_dev, "unexpected NETDEV_REGISTER event\n"); 43962306a36Sopenharmony_ci break; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci case NETDEV_UNREGISTER: 44262306a36Sopenharmony_ci ib_unregister_device_queued(&sdev->base_dev); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci case NETDEV_CHANGEADDR: 44662306a36Sopenharmony_ci siw_port_event(sdev, 1, IB_EVENT_LID_CHANGE); 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Todo: Below netdev events are currently not handled. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 45262306a36Sopenharmony_ci case NETDEV_CHANGE: 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci default: 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci ib_device_put(&sdev->base_dev); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return NOTIFY_OK; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic struct notifier_block siw_netdev_nb = { 46462306a36Sopenharmony_ci .notifier_call = siw_netdev_event, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int siw_newlink(const char *basedev_name, struct net_device *netdev) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct ib_device *base_dev; 47062306a36Sopenharmony_ci struct siw_device *sdev = NULL; 47162306a36Sopenharmony_ci int rv = -ENOMEM; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!siw_dev_qualified(netdev)) 47462306a36Sopenharmony_ci return -EINVAL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci base_dev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_SIW); 47762306a36Sopenharmony_ci if (base_dev) { 47862306a36Sopenharmony_ci ib_device_put(base_dev); 47962306a36Sopenharmony_ci return -EEXIST; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci sdev = siw_device_create(netdev); 48262306a36Sopenharmony_ci if (sdev) { 48362306a36Sopenharmony_ci dev_dbg(&netdev->dev, "siw: new device\n"); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (netif_running(netdev) && netif_carrier_ok(netdev)) 48662306a36Sopenharmony_ci sdev->state = IB_PORT_ACTIVE; 48762306a36Sopenharmony_ci else 48862306a36Sopenharmony_ci sdev->state = IB_PORT_DOWN; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci rv = siw_device_register(sdev, basedev_name); 49162306a36Sopenharmony_ci if (rv) 49262306a36Sopenharmony_ci ib_dealloc_device(&sdev->base_dev); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return rv; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct rdma_link_ops siw_link_ops = { 49862306a36Sopenharmony_ci .type = "siw", 49962306a36Sopenharmony_ci .newlink = siw_newlink, 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * siw_init_module - Initialize Softiwarp module and register with netdev 50462306a36Sopenharmony_ci * subsystem. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic __init int siw_init_module(void) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int rv; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (SENDPAGE_THRESH < SIW_MAX_INLINE) { 51162306a36Sopenharmony_ci pr_info("siw: sendpage threshold too small: %u\n", 51262306a36Sopenharmony_ci (int)SENDPAGE_THRESH); 51362306a36Sopenharmony_ci rv = -EINVAL; 51462306a36Sopenharmony_ci goto out_error; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci rv = siw_init_cpulist(); 51762306a36Sopenharmony_ci if (rv) 51862306a36Sopenharmony_ci goto out_error; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci rv = siw_cm_init(); 52162306a36Sopenharmony_ci if (rv) 52262306a36Sopenharmony_ci goto out_error; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!siw_create_tx_threads()) { 52562306a36Sopenharmony_ci pr_info("siw: Could not start any TX thread\n"); 52662306a36Sopenharmony_ci rv = -ENOMEM; 52762306a36Sopenharmony_ci goto out_error; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci /* 53062306a36Sopenharmony_ci * Locate CRC32 algorithm. If unsuccessful, fail 53162306a36Sopenharmony_ci * loading siw only, if CRC is required. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci siw_crypto_shash = crypto_alloc_shash("crc32c", 0, 0); 53462306a36Sopenharmony_ci if (IS_ERR(siw_crypto_shash)) { 53562306a36Sopenharmony_ci pr_info("siw: Loading CRC32c failed: %ld\n", 53662306a36Sopenharmony_ci PTR_ERR(siw_crypto_shash)); 53762306a36Sopenharmony_ci siw_crypto_shash = NULL; 53862306a36Sopenharmony_ci if (mpa_crc_required) { 53962306a36Sopenharmony_ci rv = -EOPNOTSUPP; 54062306a36Sopenharmony_ci goto out_error; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci rv = register_netdevice_notifier(&siw_netdev_nb); 54462306a36Sopenharmony_ci if (rv) 54562306a36Sopenharmony_ci goto out_error; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci rdma_link_register(&siw_link_ops); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci pr_info("SoftiWARP attached\n"); 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ciout_error: 55362306a36Sopenharmony_ci siw_stop_tx_threads(); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (siw_crypto_shash) 55662306a36Sopenharmony_ci crypto_free_shash(siw_crypto_shash); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pr_info("SoftIWARP attach failed. Error: %d\n", rv); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci siw_cm_exit(); 56162306a36Sopenharmony_ci siw_destroy_cpulist(); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return rv; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void __exit siw_exit_module(void) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci siw_stop_tx_threads(); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci unregister_netdevice_notifier(&siw_netdev_nb); 57162306a36Sopenharmony_ci rdma_link_unregister(&siw_link_ops); 57262306a36Sopenharmony_ci ib_unregister_driver(RDMA_DRIVER_SIW); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci siw_cm_exit(); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci siw_destroy_cpulist(); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (siw_crypto_shash) 57962306a36Sopenharmony_ci crypto_free_shash(siw_crypto_shash); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci pr_info("SoftiWARP detached\n"); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cimodule_init(siw_init_module); 58562306a36Sopenharmony_cimodule_exit(siw_exit_module); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ciMODULE_ALIAS_RDMA_LINK("siw"); 588