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