18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 38c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright(c) 2012 Intel Corporation. All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 118c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * BSD LICENSE 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Copyright(c) 2012 Intel Corporation. All rights reserved. 178c2ecf20Sopenharmony_ci * Copyright (C) 2015 EMC Corporation. All Rights Reserved. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 208c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 218c2ecf20Sopenharmony_ci * are met: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 248c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 258c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 268c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 278c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 288c2ecf20Sopenharmony_ci * distribution. 298c2ecf20Sopenharmony_ci * * Neither the name of Intel Corporation nor the names of its 308c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 318c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 348c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 358c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 368c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 378c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 388c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 398c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 408c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 418c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 428c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 438c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * PCIe NTB Network Linux driver 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Contact Information: 488c2ecf20Sopenharmony_ci * Jon Mason <jon.mason@intel.com> 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 518c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 528c2ecf20Sopenharmony_ci#include <linux/module.h> 538c2ecf20Sopenharmony_ci#include <linux/pci.h> 548c2ecf20Sopenharmony_ci#include <linux/ntb.h> 558c2ecf20Sopenharmony_ci#include <linux/ntb_transport.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define NTB_NETDEV_VER "0.7" 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(KBUILD_MODNAME); 608c2ecf20Sopenharmony_ciMODULE_VERSION(NTB_NETDEV_VER); 618c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Time in usecs for tx resource reaper */ 658c2ecf20Sopenharmony_cistatic unsigned int tx_time = 1; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Number of descriptors to free before resuming tx */ 688c2ecf20Sopenharmony_cistatic unsigned int tx_start = 10; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Number of descriptors still available before stop upper layer tx */ 718c2ecf20Sopenharmony_cistatic unsigned int tx_stop = 5; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct ntb_netdev { 748c2ecf20Sopenharmony_ci struct pci_dev *pdev; 758c2ecf20Sopenharmony_ci struct net_device *ndev; 768c2ecf20Sopenharmony_ci struct ntb_transport_qp *qp; 778c2ecf20Sopenharmony_ci struct timer_list tx_timer; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define NTB_TX_TIMEOUT_MS 1000 818c2ecf20Sopenharmony_ci#define NTB_RXQ_SIZE 100 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void ntb_netdev_event_handler(void *data, int link_is_up) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct net_device *ndev = data; 868c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci netdev_dbg(ndev, "Event %x, Link %x\n", link_is_up, 898c2ecf20Sopenharmony_ci ntb_transport_link_query(dev->qp)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (link_is_up) { 928c2ecf20Sopenharmony_ci if (ntb_transport_link_query(dev->qp)) 938c2ecf20Sopenharmony_ci netif_carrier_on(ndev); 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, 1008c2ecf20Sopenharmony_ci void *data, int len) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct net_device *ndev = qp_data; 1038c2ecf20Sopenharmony_ci struct sk_buff *skb; 1048c2ecf20Sopenharmony_ci int rc; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci skb = data; 1078c2ecf20Sopenharmony_ci if (!skb) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (len < 0) { 1138c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 1148c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors++; 1158c2ecf20Sopenharmony_ci goto enqueue_again; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci skb_put(skb, len); 1198c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 1208c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (netif_rx(skb) == NET_RX_DROP) { 1238c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 1248c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 1258c2ecf20Sopenharmony_ci } else { 1268c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 1278c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += len; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); 1318c2ecf20Sopenharmony_ci if (!skb) { 1328c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 1338c2ecf20Sopenharmony_ci ndev->stats.rx_frame_errors++; 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cienqueue_again: 1388c2ecf20Sopenharmony_ci rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); 1398c2ecf20Sopenharmony_ci if (rc) { 1408c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1418c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 1428c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int __ntb_netdev_maybe_stop_tx(struct net_device *netdev, 1478c2ecf20Sopenharmony_ci struct ntb_transport_qp *qp, int size) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(netdev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 1528c2ecf20Sopenharmony_ci /* Make sure to see the latest value of ntb_transport_tx_free_entry() 1538c2ecf20Sopenharmony_ci * since the queue was last started. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci smp_mb(); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (likely(ntb_transport_tx_free_entry(qp) < size)) { 1588c2ecf20Sopenharmony_ci mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time)); 1598c2ecf20Sopenharmony_ci return -EBUSY; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci netif_start_queue(netdev); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int ntb_netdev_maybe_stop_tx(struct net_device *ndev, 1678c2ecf20Sopenharmony_ci struct ntb_transport_qp *qp, int size) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev) || 1708c2ecf20Sopenharmony_ci (ntb_transport_tx_free_entry(qp) >= size)) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return __ntb_netdev_maybe_stop_tx(ndev, qp, size); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data, 1778c2ecf20Sopenharmony_ci void *data, int len) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct net_device *ndev = qp_data; 1808c2ecf20Sopenharmony_ci struct sk_buff *skb; 1818c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci skb = data; 1848c2ecf20Sopenharmony_ci if (!skb || !ndev) 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (len > 0) { 1888c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 1898c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 1908c2ecf20Sopenharmony_ci } else { 1918c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 1928c2ecf20Sopenharmony_ci ndev->stats.tx_aborted_errors++; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (ntb_transport_tx_free_entry(dev->qp) >= tx_start) { 1988c2ecf20Sopenharmony_ci /* Make sure anybody stopping the queue after this sees the new 1998c2ecf20Sopenharmony_ci * value of ntb_transport_tx_free_entry() 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci smp_mb(); 2028c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev)) 2038c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb, 2088c2ecf20Sopenharmony_ci struct net_device *ndev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 2118c2ecf20Sopenharmony_ci int rc; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci rc = ntb_transport_tx_enqueue(dev->qp, skb, skb->data, skb->len); 2168c2ecf20Sopenharmony_ci if (rc) 2178c2ecf20Sopenharmony_ci goto err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* check for next submit */ 2208c2ecf20Sopenharmony_ci ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cierr: 2258c2ecf20Sopenharmony_ci ndev->stats.tx_dropped++; 2268c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 2278c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void ntb_netdev_tx_timer(struct timer_list *t) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct ntb_netdev *dev = from_timer(dev, t, tx_timer); 2338c2ecf20Sopenharmony_ci struct net_device *ndev = dev->ndev; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (ntb_transport_tx_free_entry(dev->qp) < tx_stop) { 2368c2ecf20Sopenharmony_ci mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time)); 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci /* Make sure anybody stopping the queue after this sees the new 2398c2ecf20Sopenharmony_ci * value of ntb_transport_tx_free_entry() 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci smp_mb(); 2428c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev)) 2438c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int ntb_netdev_open(struct net_device *ndev) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 2508c2ecf20Sopenharmony_ci struct sk_buff *skb; 2518c2ecf20Sopenharmony_ci int rc, i, len; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Add some empty rx bufs */ 2548c2ecf20Sopenharmony_ci for (i = 0; i < NTB_RXQ_SIZE; i++) { 2558c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN); 2568c2ecf20Sopenharmony_ci if (!skb) { 2578c2ecf20Sopenharmony_ci rc = -ENOMEM; 2588c2ecf20Sopenharmony_ci goto err; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, 2628c2ecf20Sopenharmony_ci ndev->mtu + ETH_HLEN); 2638c2ecf20Sopenharmony_ci if (rc) { 2648c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2658c2ecf20Sopenharmony_ci goto err; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci timer_setup(&dev->tx_timer, ntb_netdev_tx_timer, 0); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 2728c2ecf20Sopenharmony_ci ntb_transport_link_up(dev->qp); 2738c2ecf20Sopenharmony_ci netif_start_queue(ndev); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cierr: 2788c2ecf20Sopenharmony_ci while ((skb = ntb_transport_rx_remove(dev->qp, &len))) 2798c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2808c2ecf20Sopenharmony_ci return rc; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int ntb_netdev_close(struct net_device *ndev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 2868c2ecf20Sopenharmony_ci struct sk_buff *skb; 2878c2ecf20Sopenharmony_ci int len; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ntb_transport_link_down(dev->qp); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci while ((skb = ntb_transport_rx_remove(dev->qp, &len))) 2928c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci del_timer_sync(&dev->tx_timer); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int ntb_netdev_change_mtu(struct net_device *ndev, int new_mtu) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 3028c2ecf20Sopenharmony_ci struct sk_buff *skb; 3038c2ecf20Sopenharmony_ci int len, rc; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (new_mtu > ntb_transport_max_size(dev->qp) - ETH_HLEN) 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!netif_running(ndev)) { 3098c2ecf20Sopenharmony_ci ndev->mtu = new_mtu; 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Bring down the link and dispose of posted rx entries */ 3148c2ecf20Sopenharmony_ci ntb_transport_link_down(dev->qp); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (ndev->mtu < new_mtu) { 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; (skb = ntb_transport_rx_remove(dev->qp, &len)); i++) 3208c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (; i; i--) { 3238c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(ndev, new_mtu + ETH_HLEN); 3248c2ecf20Sopenharmony_ci if (!skb) { 3258c2ecf20Sopenharmony_ci rc = -ENOMEM; 3268c2ecf20Sopenharmony_ci goto err; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, 3308c2ecf20Sopenharmony_ci new_mtu + ETH_HLEN); 3318c2ecf20Sopenharmony_ci if (rc) { 3328c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3338c2ecf20Sopenharmony_ci goto err; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ndev->mtu = new_mtu; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci ntb_transport_link_up(dev->qp); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cierr: 3458c2ecf20Sopenharmony_ci ntb_transport_link_down(dev->qp); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci while ((skb = ntb_transport_rx_remove(dev->qp, &len))) 3488c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci netdev_err(ndev, "Error changing MTU, device inoperable\n"); 3518c2ecf20Sopenharmony_ci return rc; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct net_device_ops ntb_netdev_ops = { 3558c2ecf20Sopenharmony_ci .ndo_open = ntb_netdev_open, 3568c2ecf20Sopenharmony_ci .ndo_stop = ntb_netdev_close, 3578c2ecf20Sopenharmony_ci .ndo_start_xmit = ntb_netdev_start_xmit, 3588c2ecf20Sopenharmony_ci .ndo_change_mtu = ntb_netdev_change_mtu, 3598c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void ntb_get_drvinfo(struct net_device *ndev, 3638c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); 3688c2ecf20Sopenharmony_ci strlcpy(info->version, NTB_NETDEV_VER, sizeof(info->version)); 3698c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(dev->pdev), sizeof(info->bus_info)); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int ntb_get_link_ksettings(struct net_device *dev, 3738c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(cmd, supported); 3768c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane); 3778c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(cmd, advertising); 3788c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(cmd, advertising, Backplane); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 3818c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_FULL; 3828c2ecf20Sopenharmony_ci cmd->base.port = PORT_OTHER; 3838c2ecf20Sopenharmony_ci cmd->base.phy_address = 0; 3848c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic const struct ethtool_ops ntb_ethtool_ops = { 3908c2ecf20Sopenharmony_ci .get_drvinfo = ntb_get_drvinfo, 3918c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 3928c2ecf20Sopenharmony_ci .get_link_ksettings = ntb_get_link_ksettings, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct ntb_queue_handlers ntb_netdev_handlers = { 3968c2ecf20Sopenharmony_ci .tx_handler = ntb_netdev_tx_handler, 3978c2ecf20Sopenharmony_ci .rx_handler = ntb_netdev_rx_handler, 3988c2ecf20Sopenharmony_ci .event_handler = ntb_netdev_event_handler, 3998c2ecf20Sopenharmony_ci}; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int ntb_netdev_probe(struct device *client_dev) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct ntb_dev *ntb; 4048c2ecf20Sopenharmony_ci struct net_device *ndev; 4058c2ecf20Sopenharmony_ci struct pci_dev *pdev; 4068c2ecf20Sopenharmony_ci struct ntb_netdev *dev; 4078c2ecf20Sopenharmony_ci int rc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci ntb = dev_ntb(client_dev->parent); 4108c2ecf20Sopenharmony_ci pdev = ntb->pdev; 4118c2ecf20Sopenharmony_ci if (!pdev) 4128c2ecf20Sopenharmony_ci return -ENODEV; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(*dev)); 4158c2ecf20Sopenharmony_ci if (!ndev) 4168c2ecf20Sopenharmony_ci return -ENOMEM; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, client_dev); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci dev = netdev_priv(ndev); 4218c2ecf20Sopenharmony_ci dev->ndev = ndev; 4228c2ecf20Sopenharmony_ci dev->pdev = pdev; 4238c2ecf20Sopenharmony_ci ndev->features = NETIF_F_HIGHDMA; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ndev->hw_features = ndev->features; 4288c2ecf20Sopenharmony_ci ndev->watchdog_timeo = msecs_to_jiffies(NTB_TX_TIMEOUT_MS); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci eth_random_addr(ndev->perm_addr); 4318c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, ndev->perm_addr, ndev->addr_len); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ndev->netdev_ops = &ntb_netdev_ops; 4348c2ecf20Sopenharmony_ci ndev->ethtool_ops = &ntb_ethtool_ops; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ndev->min_mtu = 0; 4378c2ecf20Sopenharmony_ci ndev->max_mtu = ETH_MAX_MTU; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci dev->qp = ntb_transport_create_queue(ndev, client_dev, 4408c2ecf20Sopenharmony_ci &ntb_netdev_handlers); 4418c2ecf20Sopenharmony_ci if (!dev->qp) { 4428c2ecf20Sopenharmony_ci rc = -EIO; 4438c2ecf20Sopenharmony_ci goto err; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ndev->mtu = ntb_transport_max_size(dev->qp) - ETH_HLEN; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci rc = register_netdev(ndev); 4498c2ecf20Sopenharmony_ci if (rc) 4508c2ecf20Sopenharmony_ci goto err1; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci dev_set_drvdata(client_dev, ndev); 4538c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s created\n", ndev->name); 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cierr1: 4578c2ecf20Sopenharmony_ci ntb_transport_free_queue(dev->qp); 4588c2ecf20Sopenharmony_cierr: 4598c2ecf20Sopenharmony_ci free_netdev(ndev); 4608c2ecf20Sopenharmony_ci return rc; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void ntb_netdev_remove(struct device *client_dev) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(client_dev); 4668c2ecf20Sopenharmony_ci struct ntb_netdev *dev = netdev_priv(ndev); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci unregister_netdev(ndev); 4698c2ecf20Sopenharmony_ci ntb_transport_free_queue(dev->qp); 4708c2ecf20Sopenharmony_ci free_netdev(ndev); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic struct ntb_transport_client ntb_netdev_client = { 4748c2ecf20Sopenharmony_ci .driver.name = KBUILD_MODNAME, 4758c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 4768c2ecf20Sopenharmony_ci .probe = ntb_netdev_probe, 4778c2ecf20Sopenharmony_ci .remove = ntb_netdev_remove, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int __init ntb_netdev_init_module(void) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci int rc; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci rc = ntb_transport_register_client_dev(KBUILD_MODNAME); 4858c2ecf20Sopenharmony_ci if (rc) 4868c2ecf20Sopenharmony_ci return rc; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci rc = ntb_transport_register_client(&ntb_netdev_client); 4898c2ecf20Sopenharmony_ci if (rc) { 4908c2ecf20Sopenharmony_ci ntb_transport_unregister_client_dev(KBUILD_MODNAME); 4918c2ecf20Sopenharmony_ci return rc; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_cimodule_init(ntb_netdev_init_module); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void __exit ntb_netdev_exit_module(void) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci ntb_transport_unregister_client(&ntb_netdev_client); 5018c2ecf20Sopenharmony_ci ntb_transport_unregister_client_dev(KBUILD_MODNAME); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_cimodule_exit(ntb_netdev_exit_module); 504