get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/814978/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 814978,
    "url": "http://patchwork.ozlabs.org/api/patches/814978/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20170918153049.44185-17-mika.westerberg@linux.intel.com/",
    "project": {
        "id": 7,
        "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api",
        "name": "Linux network development",
        "link_name": "netdev",
        "list_id": "netdev.vger.kernel.org",
        "list_email": "netdev@vger.kernel.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170918153049.44185-17-mika.westerberg@linux.intel.com>",
    "list_archive_url": null,
    "date": "2017-09-18T15:30:49",
    "name": "[16/16] thunderbolt: Add support for networking over Thunderbolt cable",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": true,
    "hash": "81a7c882c45ece5c4e40d083bcbe7154e812dec4",
    "submitter": {
        "id": 14534,
        "url": "http://patchwork.ozlabs.org/api/people/14534/?format=api",
        "name": "Mika Westerberg",
        "email": "mika.westerberg@linux.intel.com"
    },
    "delegate": {
        "id": 34,
        "url": "http://patchwork.ozlabs.org/api/users/34/?format=api",
        "username": "davem",
        "first_name": "David",
        "last_name": "Miller",
        "email": "davem@davemloft.net"
    },
    "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/20170918153049.44185-17-mika.westerberg@linux.intel.com/mbox/",
    "series": [
        {
            "id": 3664,
            "url": "http://patchwork.ozlabs.org/api/series/3664/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=3664",
            "date": "2017-09-18T15:30:47",
            "name": "Thunderbolt networking",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/3664/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/814978/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/814978/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<netdev-owner@vger.kernel.org>",
        "X-Original-To": "patchwork-incoming@ozlabs.org",
        "Delivered-To": "patchwork-incoming@ozlabs.org",
        "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)",
        "Received": [
            "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xwqlf5X7Yz9s78\n\tfor <patchwork-incoming@ozlabs.org>;\n\tTue, 19 Sep 2017 01:33:18 +1000 (AEST)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1755970AbdIRPdH (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tMon, 18 Sep 2017 11:33:07 -0400",
            "from mga11.intel.com ([192.55.52.93]:27612 \"EHLO mga11.intel.com\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1755529AbdIRPbD (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tMon, 18 Sep 2017 11:31:03 -0400",
            "from fmsmga004.fm.intel.com ([10.253.24.48])\n\tby fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n\t18 Sep 2017 08:31:02 -0700",
            "from black.fi.intel.com ([10.237.72.28])\n\tby fmsmga004.fm.intel.com with ESMTP; 18 Sep 2017 08:30:58 -0700",
            "by black.fi.intel.com (Postfix, from userid 1001)\n\tid 20F21779; Mon, 18 Sep 2017 18:30:50 +0300 (EEST)"
        ],
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.42,413,1500966000\"; d=\"scan'208\";a=\"312962629\"",
        "From": "Mika Westerberg <mika.westerberg@linux.intel.com>",
        "To": "Greg Kroah-Hartman <gregkh@linuxfoundation.org>,\n\t\"David S . Miller\" <davem@davemloft.net>",
        "Cc": "Andreas Noever <andreas.noever@gmail.com>,\n\tMichael Jamet <michael.jamet@intel.com>,\n\tYehezkel Bernat <yehezkel.bernat@intel.com>,\n\tAmir Levy <amir.jer.levy@intel.com>,\n\tMario.Limonciello@dell.com, Lukas Wunner <lukas@wunner.de>,\n\tAndy Shevchenko <andriy.shevchenko@linux.intel.com>,\n\tMika Westerberg <mika.westerberg@linux.intel.com>,\n\tlinux-kernel@vger.kernel.org, netdev@vger.kernel.org",
        "Subject": "[PATCH 16/16] thunderbolt: Add support for networking over\n\tThunderbolt cable",
        "Date": "Mon, 18 Sep 2017 18:30:49 +0300",
        "Message-Id": "<20170918153049.44185-17-mika.westerberg@linux.intel.com>",
        "X-Mailer": "git-send-email 2.14.1",
        "In-Reply-To": "<20170918153049.44185-1-mika.westerberg@linux.intel.com>",
        "References": "<20170918153049.44185-1-mika.westerberg@linux.intel.com>",
        "Sender": "netdev-owner@vger.kernel.org",
        "Precedence": "bulk",
        "List-ID": "<netdev.vger.kernel.org>",
        "X-Mailing-List": "netdev@vger.kernel.org"
    },
    "content": "From: Amir Levy <amir.jer.levy@intel.com>\n\nThunderboltIP is a protocol created by Apple to tunnel IP/ethernet\ntraffic over a Thunderbolt cable. The protocol consists of configuration\nphase where each side sends ThunderboltIP login packets (the protocol is\ndetermined by UUID in the XDomain packet header) over the configuration\nchannel. Once both sides get positive acknowledgment to their login\npacket, they configure high-speed DMA path accordingly. This DMA path is\nthen used to transmit and receive networking traffic.\n\nThis patch creates a virtual ethernet interface the host software can\nuse in the same way as any other networking interface. Once the\ninterface is brought up successfully network packets get tunneled over\nthe Thunderbolt cable to the remote host and back.\n\nThe connection is terminated by sending a ThunderboltIP logout packet\nover the configuration channel. We do this when the network interface is\nbrought down by user or the driver is unloaded.\n\nSigned-off-by: Amir Levy <amir.jer.levy@intel.com>\nSigned-off-by: Michael Jamet <michael.jamet@intel.com>\nSigned-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>\nReviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com>\n---\n Documentation/admin-guide/thunderbolt.rst |   24 +\n drivers/thunderbolt/Kconfig               |   12 +\n drivers/thunderbolt/Makefile              |    3 +\n drivers/thunderbolt/net.c                 | 1392 +++++++++++++++++++++++++++++\n 4 files changed, 1431 insertions(+)\n create mode 100644 drivers/thunderbolt/net.c",
    "diff": "diff --git a/Documentation/admin-guide/thunderbolt.rst b/Documentation/admin-guide/thunderbolt.rst\nindex 6a4cd1f159ca..5c62d11d77e8 100644\n--- a/Documentation/admin-guide/thunderbolt.rst\n+++ b/Documentation/admin-guide/thunderbolt.rst\n@@ -197,3 +197,27 @@ information is missing.\n \n To recover from this mode, one needs to flash a valid NVM image to the\n host host controller in the same way it is done in the previous chapter.\n+\n+Networking over Thunderbolt cable\n+---------------------------------\n+Thunderbolt technology allows software communication across two hosts\n+connected by a Thunderbolt cable.\n+\n+It is possible to tunnel any kind of traffic over Thunderbolt link but\n+currently we only support Apple ThunderboltIP protocol.\n+\n+If the other host is running Windows or macOS only thing you need to\n+do is to connect Thunderbolt cable between the two hosts, the\n+``thunderbolt-net`` is loaded automatically. If the other host is also\n+Linux you should load ``thunderbolt-net`` manually on one host (it does\n+not matter which one)::\n+\n+  # modprobe thunderbolt-net\n+\n+This triggers module load on the other host automatically. If the driver\n+is built-in to the kernel image, there is no need to do anything.\n+\n+The driver will create one virtual ethernet interface per Thunderbolt\n+port which are named like ``thunderbolt0`` and so on. From this point\n+you can either use standard userspace tools like ``ifconfig`` to\n+configure the interface or let your GUI to handle it automatically.\ndiff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig\nindex f4869c38c7e4..c544c751025c 100644\n--- a/drivers/thunderbolt/Kconfig\n+++ b/drivers/thunderbolt/Kconfig\n@@ -14,3 +14,15 @@ menuconfig THUNDERBOLT\n \n \t  To compile this driver a module, choose M here. The module will be\n \t  called thunderbolt.\n+\n+config THUNDERBOLT_NET\n+\ttristate \"Networking over Thunderbolt cable\"\n+\tdepends on THUNDERBOLT && INET\n+\thelp\n+\t  Select this if you want to create network between two\n+\t  computers over a Thunderbolt cable. The driver supports Apple\n+\t  ThunderboltIP protocol and allows communication with any host\n+\t  supporting the same protocol including Windows and macOS.\n+\n+\t  To compile this driver a module, choose M here. The module will be\n+\t  called thunderbolt-net.\ndiff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile\nindex f2f0de27252b..4018031db0a3 100644\n--- a/drivers/thunderbolt/Makefile\n+++ b/drivers/thunderbolt/Makefile\n@@ -1,3 +1,6 @@\n obj-${CONFIG_THUNDERBOLT} := thunderbolt.o\n thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o\n thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o\n+\n+thunderbolt-net-y += net.o\n+obj-$(CONFIG_THUNDERBOLT_NET) += thunderbolt-net.o\ndiff --git a/drivers/thunderbolt/net.c b/drivers/thunderbolt/net.c\nnew file mode 100644\nindex 000000000000..a5bfa3916df7\n--- /dev/null\n+++ b/drivers/thunderbolt/net.c\n@@ -0,0 +1,1392 @@\n+/*\n+ * Networking over Thunderbolt cable using Apple ThunderboltIP protocol\n+ *\n+ * Copyright (C) 2017, Intel Corporation\n+ * Authors: Amir Levy <amir.jer.levy@intel.com>\n+ *          Michael Jamet <michael.jamet@intel.com>\n+ *          Mika Westerberg <mika.westerberg@linux.intel.com>\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 as\n+ * published by the Free Software Foundation.\n+ */\n+\n+#include <linux/atomic.h>\n+#include <linux/highmem.h>\n+#include <linux/if_vlan.h>\n+#include <linux/jhash.h>\n+#include <linux/module.h>\n+#include <linux/etherdevice.h>\n+#include <linux/rtnetlink.h>\n+#include <linux/sizes.h>\n+#include <linux/thunderbolt.h>\n+#include <linux/uuid.h>\n+#include <linux/workqueue.h>\n+#include <net/ip6_checksum.h>\n+\n+/* Protocol timeouts in ms */\n+#define TBNET_LOGIN_DELAY\t4500\n+#define TBNET_LOGIN_TIMEOUT\t500\n+#define TBNET_LOGOUT_TIMEOUT\t100\n+\n+#define TBNET_RING_SIZE\t\t256\n+#define TBNET_LOCAL_PATH\t0xf\n+#define TBNET_RX_HDR_SIZE\t256\n+#define TBNET_LOGIN_RETRIES\t60\n+#define TBNET_LOGOUT_RETRIES\t5\n+#define TBNET_MATCH_FRAGS_ID\tBIT(1)\n+#define TBNET_MAX_MTU\t\tSZ_64K\n+#define TBNET_FRAME_SIZE\tSZ_4K\n+#define TBNET_MAX_PAYLOAD_SIZE\t\t\\\n+\t(TBNET_FRAME_SIZE - sizeof(struct thunderbolt_ip_frame_header))\n+\n+#define TBNET_L0_PORT_NUM(route) ((route) & GENMASK(5, 0))\n+\n+/**\n+ * struct thunderbolt_ip_frame_header - Header for each Thunderbolt frame\n+ * @frame_size: size of the data with the frame\n+ * @frame_index: running index on the frames\n+ * @frame_id: ID of the frame to match frames to specific packet\n+ * @frame_count: how many frames assembles a full packet\n+ *\n+ * Each data frame passed to the high-speed DMA ring has this header. If\n+ * the XDomain network directory announces that %TBNET_MATCH_FRAGS_ID is\n+ * supported then @frame_id is filled, otherwise it stays %0.\n+ */\n+struct thunderbolt_ip_frame_header {\n+\tu32 frame_size;\n+\tu16 frame_index;\n+\tu16 frame_id;\n+\tu32 frame_count;\n+} __packed;\n+\n+enum thunderbolt_ip_frame_pdf {\n+\tTBIP_PDF_FRAME_START = 1,\n+\tTBIP_PDF_FRAME_END,\n+};\n+\n+enum thunderbolt_ip_type {\n+\tTBIP_LOGIN,\n+\tTBIP_LOGIN_RESPONSE,\n+\tTBIP_LOGOUT,\n+\tTBIP_STATUS,\n+};\n+\n+struct thunderbolt_ip_header {\n+\tu32 route_hi;\n+\tu32 route_lo;\n+\tu32 length_sn;\n+\tuuid_t uuid;\n+\tuuid_t initiator_uuid;\n+\tuuid_t target_uuid;\n+\tu32 type;\n+\tu32 command_id;\n+} __packed;\n+\n+#define TBIP_HDR_LENGTH_MASK\t\tGENMASK(5, 0)\n+#define TBIP_HDR_SN_MASK\t\tGENMASK(28, 27)\n+#define TBIP_HDR_SN_SHIFT\t\t27\n+\n+struct thunderbolt_ip_login {\n+\tstruct thunderbolt_ip_header hdr;\n+\tu32 proto_version;\n+\tu32 transmit_path;\n+\tu32 reserved[4];\n+} __packed;\n+\n+#define TBIP_LOGIN_PROTO_VERSION\t1\n+\n+struct thunderbolt_ip_login_response {\n+\tstruct thunderbolt_ip_header hdr;\n+\tu32 status;\n+\tu32 receiver_mac[2];\n+\tu32 receiver_mac_len;\n+\tu32 reserved[4];\n+} __packed;\n+\n+struct thunderbolt_ip_logout {\n+\tstruct thunderbolt_ip_header hdr;\n+} __packed;\n+\n+struct thunderbolt_ip_status {\n+\tstruct thunderbolt_ip_header hdr;\n+\tu32 status;\n+} __packed;\n+\n+struct tbnet_stats {\n+\tu64 tx_packets;\n+\tu64 rx_packets;\n+\tu64 tx_bytes;\n+\tu64 rx_bytes;\n+\tu64 tx_errors;\n+\tu64 rx_length_errors;\n+\tu64 rx_over_errors;\n+\tu64 rx_crc_errors;\n+\tu64 rx_missed_errors;\n+};\n+\n+struct tbnet_frame {\n+\tstruct net_device *dev;\n+\tstruct page *page;\n+\tstruct ring_frame frame;\n+};\n+\n+struct tbnet_ring {\n+\tstruct tbnet_frame frames[TBNET_RING_SIZE];\n+\tunsigned int cons;\n+\tunsigned int prod;\n+\tstruct tb_ring *ring;\n+};\n+\n+/**\n+ * struct tbnet - ThunderboltIP network driver private data\n+ * @svc: XDomain service the driver is bound to\n+ * @xd: XDomain the service blongs to\n+ * @handler: ThunderboltIP configuration protocol handler\n+ * @dev: Networking device\n+ * @napi: NAPI structure for Rx polling\n+ * @stats: Network statistics\n+ * @skb: Network packet that is currently processed on Rx path\n+ * @command_id: ID used for next configuration protocol packet\n+ * @login_sent: ThunderboltIP login message successfully sent\n+ * @login_received: ThunderboltIP login message received from the remote\n+ *\t\t    host\n+ * @transmit_path: HopID the other end needs to use building the\n+ *\t\t   opposite side path.\n+ * @connection_lock: Lock serializing access to @login_sent,\n+ *\t\t     @login_received and @transmit_path.\n+ * @login_retries: Number of login retries currently done\n+ * @login_work: Worker to send ThunderboltIP login packets\n+ * @connected_work: Worker that finalizes the ThunderboltIP connection\n+ *\t\t    setup and enables DMA paths for high speed data\n+ *\t\t    transfers\n+ * @rx_hdr: Copy of the currently processed Rx frame. Used when a\n+ *\t    network packet consists of multiple Thunderbolt frames.\n+ *\t    In host byte order.\n+ * @rx_ring: Software ring holding Rx frames\n+ * @frame_id: Frame ID use for next Tx packet (if\n+ *\t      %TBNET_MATCH_FRAGS_ID is supported in both ends)\n+ * @tx_ring: Software ring holding Tx frames\n+ */\n+struct tbnet {\n+\tconst struct tb_service *svc;\n+\tstruct tb_xdomain *xd;\n+\tstruct tb_protocol_handler handler;\n+\tstruct net_device *dev;\n+\tstruct napi_struct napi;\n+\tstruct tbnet_stats stats;\n+\tstruct sk_buff *skb;\n+\tatomic_t command_id;\n+\tbool login_sent;\n+\tbool login_received;\n+\tu32 transmit_path;\n+\tstruct mutex connection_lock;\n+\tint login_retries;\n+\tstruct delayed_work login_work;\n+\tstruct work_struct connected_work;\n+\tstruct thunderbolt_ip_frame_header rx_hdr;\n+\tstruct tbnet_ring rx_ring;\n+\tatomic_t frame_id;\n+\tstruct tbnet_ring tx_ring;\n+};\n+\n+/* Network property directory UUID */\n+static const uuid_t tbnet_dir_uuid =\n+\tUUID_INIT(0xc66189ca, 0x1cce, 0x4195,\n+\t\t  0xbd, 0xb8, 0x49, 0x59, 0x2e, 0x5f, 0x5a, 0x4f);\n+\n+/* ThunderboltIP configuration protocol UUID */\n+static const uuid_t tbnet_svc_uuid =\n+\tUUID_INIT(0x798f589e, 0x3616, 0x8a47,\n+\t\t  0x97, 0xc6, 0x56, 0x64, 0xa9, 0x20, 0xc8, 0xdd);\n+\n+static struct tb_property_dir *tbnet_dir;\n+\n+static void tbnet_fill_header(struct thunderbolt_ip_header *hdr, u64 route,\n+\tu8 sequence, const uuid_t *initiator_uuid, const uuid_t *target_uuid,\n+\tenum thunderbolt_ip_type type, size_t size, u32 command_id)\n+{\n+\tu32 length_sn;\n+\n+\t/* Length does not include route_hi/lo and length_sn fields */\n+\tlength_sn = (size - 3 * 4) / 4;\n+\tlength_sn |= (sequence << TBIP_HDR_SN_SHIFT) & TBIP_HDR_SN_MASK;\n+\n+\thdr->route_hi = upper_32_bits(route);\n+\thdr->route_lo = lower_32_bits(route);\n+\thdr->length_sn = length_sn;\n+\tuuid_copy(&hdr->uuid, &tbnet_svc_uuid);\n+\tuuid_copy(&hdr->initiator_uuid, initiator_uuid);\n+\tuuid_copy(&hdr->target_uuid, target_uuid);\n+\thdr->type = type;\n+\thdr->command_id = command_id;\n+}\n+\n+static int tbnet_login_response(struct tbnet *net, u64 route, u8 sequence,\n+\t\t\t\tu32 command_id)\n+{\n+\tstruct thunderbolt_ip_login_response reply;\n+\tstruct tb_xdomain *xd = net->xd;\n+\n+\tmemset(&reply, 0, sizeof(reply));\n+\ttbnet_fill_header(&reply.hdr, route, sequence, xd->local_uuid,\n+\t\t\t  xd->remote_uuid, TBIP_LOGIN_RESPONSE, sizeof(reply),\n+\t\t\t  command_id);\n+\tmemcpy(reply.receiver_mac, net->dev->dev_addr, ETH_ALEN);\n+\treply.receiver_mac_len = ETH_ALEN;\n+\n+\treturn tb_xdomain_response(xd, &reply, sizeof(reply),\n+\t\t\t\t   TB_CFG_PKG_XDOMAIN_RESP);\n+}\n+\n+static int tbnet_login_request(struct tbnet *net, u8 sequence)\n+{\n+\tstruct thunderbolt_ip_login_response reply;\n+\tstruct thunderbolt_ip_login request;\n+\tstruct tb_xdomain *xd = net->xd;\n+\n+\tmemset(&request, 0, sizeof(request));\n+\ttbnet_fill_header(&request.hdr, xd->route, sequence, xd->local_uuid,\n+\t\t\t  xd->remote_uuid, TBIP_LOGIN, sizeof(request),\n+\t\t\t  atomic_inc_return(&net->command_id));\n+\n+\trequest.proto_version = TBIP_LOGIN_PROTO_VERSION;\n+\trequest.transmit_path = TBNET_LOCAL_PATH;\n+\n+\treturn tb_xdomain_request(xd, &request, sizeof(request),\n+\t\t\t\t  TB_CFG_PKG_XDOMAIN_RESP, &reply,\n+\t\t\t\t  sizeof(reply), TB_CFG_PKG_XDOMAIN_RESP,\n+\t\t\t\t  TBNET_LOGIN_TIMEOUT);\n+}\n+\n+static int tbnet_logout_response(struct tbnet *net, u64 route, u8 sequence,\n+\t\t\t\t  u32 command_id)\n+{\n+\tstruct thunderbolt_ip_status reply;\n+\tstruct tb_xdomain *xd = net->xd;\n+\n+\tmemset(&reply, 0, sizeof(reply));\n+\ttbnet_fill_header(&reply.hdr, route, sequence, xd->local_uuid,\n+\t\t\t  xd->remote_uuid, TBIP_STATUS, sizeof(reply),\n+\t\t\t  atomic_inc_return(&net->command_id));\n+\treturn tb_xdomain_response(xd, &reply, sizeof(reply),\n+\t\t\t\t   TB_CFG_PKG_XDOMAIN_RESP);\n+}\n+\n+static int tbnet_logout_request(struct tbnet *net)\n+{\n+\tstruct thunderbolt_ip_logout request;\n+\tstruct thunderbolt_ip_status reply;\n+\tstruct tb_xdomain *xd = net->xd;\n+\n+\tmemset(&request, 0, sizeof(request));\n+\ttbnet_fill_header(&request.hdr, xd->route, 0, xd->local_uuid,\n+\t\t\t  xd->remote_uuid, TBIP_LOGOUT, sizeof(request),\n+\t\t\t  atomic_inc_return(&net->command_id));\n+\n+\treturn tb_xdomain_request(xd, &request, sizeof(request),\n+\t\t\t\t  TB_CFG_PKG_XDOMAIN_RESP, &reply,\n+\t\t\t\t  sizeof(reply), TB_CFG_PKG_XDOMAIN_RESP,\n+\t\t\t\t  TBNET_LOGOUT_TIMEOUT);\n+}\n+\n+static void start_login(struct tbnet *net)\n+{\n+\tmutex_lock(&net->connection_lock);\n+\tnet->login_sent = false;\n+\tnet->login_received = false;\n+\tmutex_unlock(&net->connection_lock);\n+\n+\tqueue_delayed_work(system_long_wq, &net->login_work,\n+\t\t\t   msecs_to_jiffies(1000));\n+}\n+\n+static void stop_login(struct tbnet *net)\n+{\n+\tcancel_delayed_work_sync(&net->login_work);\n+\tcancel_work_sync(&net->connected_work);\n+}\n+\n+static inline unsigned int tbnet_frame_size(const struct tbnet_frame *tf)\n+{\n+\treturn tf->frame.size ? : TBNET_FRAME_SIZE;\n+}\n+\n+static void tbnet_free_buffers(struct tbnet_ring *ring)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < TBNET_RING_SIZE; i++) {\n+\t\tstruct device *dma_dev = tb_ring_dma_device(ring->ring);\n+\t\tstruct tbnet_frame *tf = &ring->frames[i];\n+\t\tenum dma_data_direction dir;\n+\t\tsize_t size;\n+\n+\t\tif (!tf->page)\n+\t\t\tcontinue;\n+\n+\t\tif (ring->ring->is_tx) {\n+\t\t\tdir = DMA_TO_DEVICE;\n+\t\t\tsize = tbnet_frame_size(tf);\n+\t\t} else {\n+\t\t\tdir = DMA_FROM_DEVICE;\n+\t\t\tsize = TBNET_FRAME_SIZE;\n+\t\t}\n+\n+\t\tdma_unmap_page(dma_dev, tf->frame.buffer_phy, size, dir);\n+\t\t__free_page(tf->page);\n+\t\ttf->page = NULL;\n+\t}\n+\n+\tring->cons = 0;\n+\tring->prod = 0;\n+}\n+\n+static void tbnet_tear_down(struct tbnet *net, bool send_logout)\n+{\n+\tnetif_carrier_off(net->dev);\n+\tnetif_stop_queue(net->dev);\n+\n+\tstop_login(net);\n+\n+\tmutex_lock(&net->connection_lock);\n+\n+\tif (net->login_sent && net->login_received) {\n+\t\tint retries = TBNET_LOGOUT_RETRIES;\n+\n+\t\twhile (send_logout && retries-- > 0) {\n+\t\t\tint ret = tbnet_logout_request(net);\n+\t\t\tif (ret != -ETIMEDOUT)\n+\t\t\t\tbreak;\n+\t\t}\n+\n+\t\ttb_ring_stop(net->rx_ring.ring);\n+\t\ttb_ring_stop(net->tx_ring.ring);\n+\t\ttbnet_free_buffers(&net->rx_ring);\n+\t\ttbnet_free_buffers(&net->tx_ring);\n+\n+\t\tif (tb_xdomain_disable_paths(net->xd))\n+\t\t\tnetdev_warn(net->dev, \"failed to disable DMA paths\\n\");\n+\t}\n+\n+\tnet->login_retries = 0;\n+\tnet->login_sent = false;\n+\tnet->login_received = false;\n+\n+\tmutex_unlock(&net->connection_lock);\n+}\n+\n+static int tbnet_handle_packet(const void *buf, size_t size, void *data)\n+{\n+\tconst struct thunderbolt_ip_login *pkg = buf;\n+\tstruct tbnet *net = data;\n+\tu32 command_id;\n+\tint ret = 0;\n+\tu8 sequence;\n+\tu64 route;\n+\n+\t/* Make sure the packet is for us */\n+\tif (size < sizeof(struct thunderbolt_ip_header))\n+\t\treturn 0;\n+\tif (!uuid_equal(&pkg->hdr.initiator_uuid, net->xd->remote_uuid))\n+\t\treturn 0;\n+\tif (!uuid_equal(&pkg->hdr.target_uuid, net->xd->local_uuid))\n+\t\treturn 0;\n+\n+\troute = ((u64)pkg->hdr.route_hi << 32) | pkg->hdr.route_lo;\n+\troute &= ~BIT_ULL(63);\n+\tif (route != net->xd->route)\n+\t\treturn 0;\n+\n+\tsequence = pkg->hdr.length_sn & TBIP_HDR_SN_MASK;\n+\tsequence >>= TBIP_HDR_SN_SHIFT;\n+\tcommand_id = pkg->hdr.command_id;\n+\n+\tswitch (pkg->hdr.type) {\n+\tcase TBIP_LOGIN:\n+\t\tif (!netif_running(net->dev))\n+\t\t\tbreak;\n+\n+\t\tret = tbnet_login_response(net, route, sequence,\n+\t\t\t\t\t   pkg->hdr.command_id);\n+\t\tif (!ret) {\n+\t\t\tmutex_lock(&net->connection_lock);\n+\t\t\tnet->login_received = true;\n+\t\t\tnet->transmit_path = pkg->transmit_path;\n+\n+\t\t\t/*\n+\t\t\t * If we reached the number of max retries or\n+\t\t\t * previous logout, schedule another round of\n+\t\t\t * login retries\n+\t\t\t */\n+\t\t\tif (net->login_retries >= TBNET_LOGIN_RETRIES ||\n+\t\t\t    !net->login_sent) {\n+\t\t\t\tnet->login_retries = 0;\n+\t\t\t\tqueue_delayed_work(system_long_wq,\n+\t\t\t\t\t\t   &net->login_work, 0);\n+\t\t\t}\n+\t\t\tmutex_unlock(&net->connection_lock);\n+\n+\t\t\tqueue_work(system_long_wq, &net->connected_work);\n+\t\t}\n+\t\tbreak;\n+\n+\tcase TBIP_LOGOUT:\n+\t\tret = tbnet_logout_response(net, route, sequence, command_id);\n+\t\tif (!ret)\n+\t\t\ttbnet_tear_down(net, false);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\tif (ret)\n+\t\tnetdev_warn(net->dev, \"failed to send ThunderboltIP response\\n\");\n+\n+\treturn 1;\n+}\n+\n+static unsigned int tbnet_available_buffers(const struct tbnet_ring *ring)\n+{\n+\treturn ring->prod - ring->cons;\n+}\n+\n+static int tbnet_alloc_rx_buffers(struct tbnet *net, unsigned int nbuffers)\n+{\n+\tstruct tbnet_ring *ring = &net->rx_ring;\n+\tint ret;\n+\n+\twhile (nbuffers-- > 0) {\n+\t\tstruct device *dma_dev = tb_ring_dma_device(ring->ring);\n+\t\tunsigned int index = ring->prod & (TBNET_RING_SIZE - 1);\n+\t\tstruct tbnet_frame *tf = &ring->frames[index];\n+\t\tdma_addr_t dma_addr;\n+\n+\t\tif (tf->page)\n+\t\t\tbreak;\n+\n+\t\ttf->page = dev_alloc_page();\n+\t\tif (!tf->page) {\n+\t\t\tret = -ENOMEM;\n+\t\t\tgoto err_free;\n+\t\t}\n+\n+\t\tdma_addr = dma_map_page(dma_dev, tf->page, 0,\n+\t\t\t\tTBNET_FRAME_SIZE, DMA_FROM_DEVICE);\n+\t\tif (dma_mapping_error(dma_dev, dma_addr)) {\n+\t\t\tret = -ENOMEM;\n+\t\t\tgoto err_free;\n+\t\t}\n+\n+\t\ttf->frame.buffer_phy = dma_addr;\n+\t\ttf->dev = net->dev;\n+\n+\t\ttb_ring_rx(ring->ring, &tf->frame);\n+\n+\t\tring->prod++;\n+\t}\n+\n+\treturn 0;\n+\n+err_free:\n+\ttbnet_free_buffers(ring);\n+\treturn ret;\n+}\n+\n+static struct tbnet_frame *tbnet_get_tx_buffer(struct tbnet *net)\n+{\n+\tstruct tbnet_ring *ring = &net->tx_ring;\n+\tstruct tbnet_frame *tf;\n+\tunsigned int index;\n+\n+\tif (!tbnet_available_buffers(ring))\n+\t\treturn NULL;\n+\n+\tindex = ring->cons++ & (TBNET_RING_SIZE - 1);\n+\n+\ttf = &ring->frames[index];\n+\ttf->frame.size = 0;\n+\ttf->frame.buffer_phy = 0;\n+\n+\treturn tf;\n+}\n+\n+static void tbnet_tx_callback(struct tb_ring *ring, struct ring_frame *frame,\n+\t\t\t      bool canceled)\n+{\n+\tstruct tbnet_frame *tf = container_of(frame, typeof(*tf), frame);\n+\tstruct device *dma_dev = tb_ring_dma_device(ring);\n+\tstruct tbnet *net = netdev_priv(tf->dev);\n+\n+\tdma_unmap_page(dma_dev, tf->frame.buffer_phy, tbnet_frame_size(tf),\n+\t\t       DMA_TO_DEVICE);\n+\n+\t/* Return buffer to the ring */\n+\tnet->tx_ring.prod++;\n+\n+\tif (tbnet_available_buffers(&net->tx_ring) >= TBNET_RING_SIZE / 2)\n+\t\tnetif_wake_queue(net->dev);\n+}\n+\n+static int tbnet_alloc_tx_buffers(struct tbnet *net)\n+{\n+\tstruct tbnet_ring *ring = &net->tx_ring;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < TBNET_RING_SIZE; i++) {\n+\t\tstruct tbnet_frame *tf = &ring->frames[i];\n+\n+\t\ttf->page = alloc_page(GFP_KERNEL);\n+\t\tif (!tf->page) {\n+\t\t\ttbnet_free_buffers(ring);\n+\t\t\treturn -ENOMEM;\n+\t\t}\n+\n+\t\ttf->dev = net->dev;\n+\t\ttf->frame.callback = tbnet_tx_callback;\n+\t\ttf->frame.sof = TBIP_PDF_FRAME_START;\n+\t\ttf->frame.eof = TBIP_PDF_FRAME_END;\n+\t}\n+\n+\tring->cons = 0;\n+\tring->prod = TBNET_RING_SIZE - 1;\n+\n+\treturn 0;\n+}\n+\n+static void tbnet_connected_work(struct work_struct *work)\n+{\n+\tstruct tbnet *net = container_of(work, typeof(*net), connected_work);\n+\tbool connected;\n+\tint ret;\n+\n+\tif (netif_carrier_ok(net->dev))\n+\t\treturn;\n+\n+\tmutex_lock(&net->connection_lock);\n+\tconnected = net->login_sent && net->login_received;\n+\tmutex_unlock(&net->connection_lock);\n+\n+\tif (!connected)\n+\t\treturn;\n+\n+\t/*\n+\t * Both logins successful so enable the high-speed DMA paths and\n+\t * start the network device queue.\n+\t */\n+\tret = tb_xdomain_enable_paths(net->xd, TBNET_LOCAL_PATH,\n+\t\t\t\t      net->rx_ring.ring->hop,\n+\t\t\t\t      net->transmit_path,\n+\t\t\t\t      net->tx_ring.ring->hop);\n+\tif (ret) {\n+\t\tnetdev_err(net->dev, \"failed to enable DMA paths\\n\");\n+\t\treturn;\n+\t}\n+\n+\ttb_ring_start(net->tx_ring.ring);\n+\ttb_ring_start(net->rx_ring.ring);\n+\n+\tret = tbnet_alloc_rx_buffers(net, TBNET_RING_SIZE);\n+\tif (ret)\n+\t\tgoto err_stop_rings;\n+\n+\tret = tbnet_alloc_tx_buffers(net);\n+\tif (ret)\n+\t\tgoto err_free_rx_buffers;\n+\n+\tnetif_carrier_on(net->dev);\n+\tnetif_start_queue(net->dev);\n+\treturn;\n+\n+err_free_rx_buffers:\n+\ttbnet_free_buffers(&net->rx_ring);\n+err_stop_rings:\n+\ttb_ring_stop(net->rx_ring.ring);\n+\ttb_ring_stop(net->tx_ring.ring);\n+}\n+\n+static void tbnet_login_work(struct work_struct *work)\n+{\n+\tstruct tbnet *net = container_of(work, typeof(*net), login_work.work);\n+\tunsigned long delay = msecs_to_jiffies(TBNET_LOGIN_DELAY);\n+\tint ret;\n+\n+\tif (netif_carrier_ok(net->dev))\n+\t\treturn;\n+\n+\tret = tbnet_login_request(net, net->login_retries % 4);\n+\tif (ret) {\n+\t\tif (net->login_retries++ < TBNET_LOGIN_RETRIES) {\n+\t\t\tqueue_delayed_work(system_long_wq, &net->login_work,\n+\t\t\t\t\t   delay);\n+\t\t} else {\n+\t\t\tnetdev_info(net->dev, \"ThunderboltIP login timed out\\n\");\n+\t\t}\n+\t} else {\n+\t\tnet->login_retries = 0;\n+\n+\t\tmutex_lock(&net->connection_lock);\n+\t\tnet->login_sent = true;\n+\t\tmutex_unlock(&net->connection_lock);\n+\n+\t\tqueue_work(system_long_wq, &net->connected_work);\n+\t}\n+}\n+\n+static bool tbnet_check_frame(struct tbnet *net,\n+\t\t\t       const struct tbnet_frame *tf)\n+{\n+\tu32 frame_id, frame_count, frame_size, frame_index;\n+\tconst struct thunderbolt_ip_frame_header *hdr;\n+\tunsigned int size;\n+\n+\tif (tf->frame.flags & RING_DESC_CRC_ERROR) {\n+\t\tnet->stats.rx_crc_errors++;\n+\t\treturn false;\n+\t} else if (tf->frame.flags & RING_DESC_BUFFER_OVERRUN) {\n+\t\tnet->stats.rx_over_errors++;\n+\t\treturn false;\n+\t}\n+\n+\t/* Should be greater than just header i.e. contains data */\n+\tsize = tbnet_frame_size(tf);\n+\tif (size <= sizeof(*hdr)) {\n+\t\tnet->stats.rx_length_errors++;\n+\t\treturn false;\n+\t}\n+\n+\thdr = page_address(tf->page);\n+\tframe_count = le32_to_cpu(hdr->frame_count);\n+\tframe_size = le32_to_cpu(hdr->frame_size);\n+\tframe_index = le16_to_cpu(hdr->frame_index);\n+\tframe_id = le16_to_cpu(hdr->frame_id);\n+\n+\tif ((frame_size > size - sizeof(*hdr)) || !frame_size) {\n+\t\tnet->stats.rx_length_errors++;\n+\t\treturn false;\n+\t}\n+\n+\t/*\n+\t * In case we're in the middle of packet, validate the frame\n+\t * header based on first fragment of the packet.\n+\t */\n+\tif (net->skb && net->rx_hdr.frame_count) {\n+\t\t/* Check the frame count fits the count field */\n+\t\tif (frame_count != net->rx_hdr.frame_count) {\n+\t\t\tnet->stats.rx_length_errors++;\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Check the frame identifiers are incremented correctly,\n+\t\t * and id is matching.\n+\t\t */\n+\t\tif (frame_index != net->rx_hdr.frame_index + 1 ||\n+\t\t    frame_id != net->rx_hdr.frame_id) {\n+\t\t\tnet->stats.rx_missed_errors++;\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tif (net->skb->len + frame_size > TBNET_MAX_MTU) {\n+\t\t\tnet->stats.rx_length_errors++;\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\treturn true;\n+\t}\n+\n+\t/* Start of packet, validate the frame header */\n+\tif (frame_count == 0 || frame_count > TBNET_RING_SIZE / 4) {\n+\t\tnet->stats.rx_length_errors++;\n+\t\treturn false;\n+\t}\n+\tif (frame_index != 0) {\n+\t\tnet->stats.rx_missed_errors++;\n+\t\treturn false;\n+\t}\n+\tif (frame_count > 1 && frame_size < TBNET_RX_HDR_SIZE) {\n+\t\tnet->stats.rx_length_errors++;\n+\t\treturn false;\n+\t}\n+\n+\treturn true;\n+}\n+\n+static void tbnet_pull_tail(struct sk_buff *skb)\n+{\n+\tskb_frag_t *frag = &skb_shinfo(skb)->frags[0];\n+\tunsigned int pull_len;\n+\tvoid *hdr;\n+\n+\thdr = skb_frag_address(frag);\n+\tpull_len = eth_get_headlen(hdr, TBNET_RX_HDR_SIZE);\n+\n+\t/* Align pull length to size of long to optimize memcpy performance */\n+\tskb_copy_to_linear_data(skb, hdr, ALIGN(pull_len, sizeof(long)));\n+\n+\t/* Update all of the pointers */\n+\tskb_frag_size_sub(frag, pull_len);\n+\tfrag->page_offset += pull_len;\n+\tskb->data_len -= pull_len;\n+\tskb->tail += pull_len;\n+}\n+\n+static int tbnet_poll(struct napi_struct *napi, int budget)\n+{\n+\tstruct tbnet *net = container_of(napi, struct tbnet, napi);\n+\tunsigned int cleaned_count = tbnet_available_buffers(&net->rx_ring);\n+\tstruct device *dma_dev = tb_ring_dma_device(net->rx_ring.ring);\n+\tunsigned int rx_packets = 0;\n+\n+\twhile (rx_packets < budget) {\n+\t\tu32 size, frame_size, frame_count, frame_index;\n+\t\tconst struct thunderbolt_ip_frame_header *hdr;\n+\t\tunsigned int hdr_size = sizeof(*hdr);\n+\t\tstruct sk_buff *skb = NULL;\n+\t\tstruct ring_frame *frame;\n+\t\tstruct tbnet_frame *tf;\n+\t\tbool last = true;\n+\n+\t\t/*\n+\t\t * Return some buffers to hardware, one at a time is too\n+\t\t * slow so allocate MAX_SKB_FRAGS buffers at the same\n+\t\t * time.\n+\t\t */\n+\t\tif (cleaned_count >= MAX_SKB_FRAGS) {\n+\t\t\ttbnet_alloc_rx_buffers(net, cleaned_count);\n+\t\t\tcleaned_count = 0;\n+\t\t}\n+\n+\t\tframe = tb_ring_poll(net->rx_ring.ring);\n+\t\tif (!frame)\n+\t\t\tbreak;\n+\n+\t\tdma_unmap_page(dma_dev, frame->buffer_phy, TBNET_FRAME_SIZE,\n+\t\t\t       DMA_FROM_DEVICE);\n+\n+\t\ttf = container_of(frame, typeof(*tf), frame);\n+\t\tsize = tbnet_frame_size(tf);\n+\t\thdr = page_address(tf->page);\n+\n+\t\tif (!tbnet_check_frame(net, tf)) {\n+\t\t\t__free_page(tf->page);\n+\t\t\ttf->page = NULL;\n+\t\t\tnet->rx_ring.cons++;\n+\t\t\tcleaned_count++;\n+\t\t\tdev_kfree_skb_any(net->skb);\n+\t\t\tnet->skb = NULL;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tframe_count = le32_to_cpu(hdr->frame_count);\n+\t\tframe_size = le32_to_cpu(hdr->frame_size);\n+\t\tframe_index = le16_to_cpu(hdr->frame_index);\n+\t\tlast = frame_index == frame_count - 1;\n+\n+\t\tskb = net->skb;\n+\t\tif (!skb) {\n+\t\t\tskb = netdev_alloc_skb_ip_align(net->dev,\n+\t\t\t\t\t\t\tTBNET_RX_HDR_SIZE);\n+\t\t\tnet->skb = skb;\n+\t\t}\n+\t\tif (!skb)\n+\t\t\tbreak;\n+\n+\t\t/*\n+\t\t * Single small buffer we can copy directly to the\n+\t\t * header part of the skb.\n+\t\t */\n+\t\tif (hdr->frame_count == 1 && frame_size <= TBNET_RX_HDR_SIZE) {\n+\t\t\tconst void *data = hdr + 1;\n+\n+\t\t\tmemcpy(__skb_put(skb, frame_size), data,\n+\t\t\t       ALIGN(frame_size, sizeof(long)));\n+\n+\t\t\t__free_page(tf->page);\n+\t\t} else {\n+\t\t\tskb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,\n+\t\t\t\t\ttf->page, hdr_size, frame_size, size);\n+\t\t\tif (last)\n+\t\t\t\ttbnet_pull_tail(skb);\n+\t\t}\n+\n+\t\ttf->page = NULL;\n+\t\tnet->rx_ring.cons++;\n+\t\tcleaned_count++;\n+\n+\t\tnet->rx_hdr.frame_count = frame_count;\n+\t\tnet->rx_hdr.frame_size = frame_size;\n+\t\tnet->rx_hdr.frame_index = frame_index;\n+\t\tnet->rx_hdr.frame_id = le16_to_cpu(hdr->frame_id);\n+\n+\t\trx_packets++;\n+\t\tnet->stats.rx_bytes += frame_size;\n+\n+\t\tif (last) {\n+\t\t\tskb->protocol = eth_type_trans(skb, net->dev);\n+\t\t\tnapi_gro_receive(&net->napi, skb);\n+\t\t\tnet->skb = NULL;\n+\t\t}\n+\t}\n+\n+\tnet->stats.rx_packets += rx_packets;\n+\n+\tif (cleaned_count)\n+\t\ttbnet_alloc_rx_buffers(net, cleaned_count);\n+\n+\tif (rx_packets >= budget)\n+\t\treturn budget;\n+\n+\tnapi_complete_done(napi, rx_packets);\n+\t/* Re-enable the ring interrupt */\n+\ttb_ring_poll_complete(net->rx_ring.ring);\n+\n+\treturn rx_packets;\n+}\n+\n+static void tbnet_start_poll(void *data)\n+{\n+\tstruct tbnet *net = data;\n+\n+\tnapi_schedule(&net->napi);\n+}\n+\n+static int tbnet_open(struct net_device *dev)\n+{\n+\tstruct tbnet *net = netdev_priv(dev);\n+\tstruct tb_xdomain *xd = net->xd;\n+\tu16 sof_mask, eof_mask;\n+\tstruct tb_ring *ring;\n+\n+\tnetif_carrier_off(dev);\n+\n+\tring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE,\n+\t\t\t\tRING_FLAG_FRAME);\n+\tif (!ring) {\n+\t\tnetdev_err(dev, \"failed to allocate Tx ring\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\tnet->tx_ring.ring = ring;\n+\n+\tsof_mask = BIT(TBIP_PDF_FRAME_START);\n+\teof_mask = BIT(TBIP_PDF_FRAME_END);\n+\n+\tring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE,\n+\t\t\t\tRING_FLAG_FRAME | RING_FLAG_E2E, sof_mask,\n+\t\t\t\teof_mask, tbnet_start_poll, net);\n+\tif (!ring) {\n+\t\tnetdev_err(dev, \"failed to allocate Rx ring\\n\");\n+\t\ttb_ring_free(net->tx_ring.ring);\n+\t\tnet->tx_ring.ring = NULL;\n+\t\treturn -ENOMEM;\n+\t}\n+\tnet->rx_ring.ring = ring;\n+\n+\tnapi_enable(&net->napi);\n+\tstart_login(net);\n+\n+\treturn 0;\n+}\n+\n+static int tbnet_stop(struct net_device *dev)\n+{\n+\tstruct tbnet *net = netdev_priv(dev);\n+\n+\tnapi_disable(&net->napi);\n+\n+\ttbnet_tear_down(net, true);\n+\n+\ttb_ring_free(net->rx_ring.ring);\n+\tnet->rx_ring.ring = NULL;\n+\ttb_ring_free(net->tx_ring.ring);\n+\tnet->tx_ring.ring = NULL;\n+\n+\treturn 0;\n+}\n+\n+static bool tbnet_xmit_map(struct device *dma_dev, struct tbnet_frame *tf)\n+{\n+\tdma_addr_t dma_addr;\n+\n+\tdma_addr = dma_map_page(dma_dev, tf->page, 0, tbnet_frame_size(tf),\n+\t\t\t\tDMA_TO_DEVICE);\n+\tif (dma_mapping_error(dma_dev, dma_addr))\n+\t\treturn false;\n+\n+\ttf->frame.buffer_phy = dma_addr;\n+\treturn true;\n+}\n+\n+static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb,\n+\t\t\t\tstruct tbnet_frame **frames, u32 frame_count)\n+{\n+\tstruct thunderbolt_ip_frame_header *hdr = page_address(frames[0]->page);\n+\tstruct device *dma_dev = tb_ring_dma_device(net->tx_ring.ring);\n+\t__wsum wsum = htonl(skb->len - skb_transport_offset(skb));\n+\tunsigned int i, len, offset = skb_transport_offset(skb);\n+\t__be16 protocol = skb->protocol;\n+\tvoid *data = skb->data;\n+\tvoid *dest = hdr + 1;\n+\t__sum16 *tucso;\n+\n+\tif (skb->ip_summed != CHECKSUM_PARTIAL) {\n+\t\t/*\n+\t\t * No need to calculate checksum so we just update the\n+\t\t * total frame count and map the frames for DMA.\n+\t\t */\n+\t\tfor (i = 0; i < frame_count; i++) {\n+\t\t\thdr = page_address(frames[i]->page);\n+\t\t\thdr->frame_count = cpu_to_le32(frame_count);\n+\t\t\tif (!tbnet_xmit_map(dma_dev, frames[i]))\n+\t\t\t\tgoto err_unmap;\n+\t\t}\n+\n+\t\treturn true;\n+\t}\n+\n+\tif (protocol == htons(ETH_P_8021Q)) {\n+\t\tstruct vlan_hdr *vhdr, vh;\n+\n+\t\tvhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(vh), &vh);\n+\t\tif (!vhdr)\n+\t\t\treturn false;\n+\n+\t\tprotocol = vhdr->h_vlan_encapsulated_proto;\n+\t}\n+\n+\t/*\n+\t * Data points on the beginning of packet.\n+\t * Check is the checksum absolute place in the packet.\n+\t * ipcso will update IP checksum.\n+\t * tucso will update TCP/UPD checksum.\n+\t */\n+\tif (protocol == htons(ETH_P_IP)) {\n+\t\t__sum16 *ipcso = dest + ((void *)&(ip_hdr(skb)->check) - data);\n+\n+\t\t*ipcso = 0;\n+\t\t*ipcso = ip_fast_csum(dest + skb_network_offset(skb),\n+\t\t\t\t      ip_hdr(skb)->ihl);\n+\n+\t\tif (ip_hdr(skb)->protocol == IPPROTO_TCP)\n+\t\t\ttucso = dest + ((void *)&(tcp_hdr(skb)->check) - data);\n+\t\telse if (ip_hdr(skb)->protocol == IPPROTO_UDP)\n+\t\t\ttucso = dest + ((void *)&(udp_hdr(skb)->check) - data);\n+\t\telse\n+\t\t\treturn false;\n+\n+\t\t*tucso = ~csum_tcpudp_magic(ip_hdr(skb)->saddr,\n+\t\t\t\t\t    ip_hdr(skb)->daddr, 0,\n+\t\t\t\t\t    ip_hdr(skb)->protocol, 0);\n+\t} else if (skb_is_gso_v6(skb)) {\n+\t\ttucso = dest + ((void *)&(tcp_hdr(skb)->check) - data);\n+\t\t*tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,\n+\t\t\t\t\t  &ipv6_hdr(skb)->daddr, 0,\n+\t\t\t\t\t  IPPROTO_TCP, 0);\n+\t\treturn false;\n+\t} else if (protocol == htons(ETH_P_IPV6)) {\n+\t\ttucso = dest + skb_checksum_start_offset(skb) + skb->csum_offset;\n+\t\t*tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,\n+\t\t\t\t\t  &ipv6_hdr(skb)->daddr, 0,\n+\t\t\t\t\t  ipv6_hdr(skb)->nexthdr, 0);\n+\t} else {\n+\t\treturn false;\n+\t}\n+\n+\t/*\n+\t * First frame was headers, rest of the frames contain data.\n+\t * Calculate checksum over each frame.\n+\t */\n+\tfor (i = 0; i < frame_count; i++) {\n+\t\thdr = page_address(frames[i]->page);\n+\t\tdest = (void *)(hdr + 1) + offset;\n+\t\tlen = le32_to_cpu(hdr->frame_size) - offset;\n+\t\twsum = csum_partial(dest, len, wsum);\n+\t\thdr->frame_count = cpu_to_le32(frame_count);\n+\n+\t\toffset = 0;\n+\t}\n+\n+\t*tucso = csum_fold(wsum);\n+\n+\t/*\n+\t * Checksum is finally calculated and we don't touch the memory\n+\t * anymore, so DMA map the frames now.\n+\t */\n+\tfor (i = 0; i < frame_count; i++) {\n+\t\tif (!tbnet_xmit_map(dma_dev, frames[i]))\n+\t\t\tgoto err_unmap;\n+\t}\n+\n+\treturn true;\n+\n+err_unmap:\n+\tfor (--i; i > 0; i--)\n+\t\tdma_unmap_page(dma_dev, frames[i]->frame.buffer_phy,\n+\t\t\t       tbnet_frame_size(frames[i]), DMA_TO_DEVICE);\n+\n+\treturn false;\n+}\n+\n+static void *tbnet_kmap_frag(struct sk_buff *skb, unsigned int frag_num,\n+\t\t\t      unsigned int *len)\n+{\n+\tconst skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_num];\n+\n+\t*len = skb_frag_size(frag);\n+\treturn kmap_atomic(skb_frag_page(frag)) + frag->page_offset;\n+}\n+\n+static netdev_tx_t tbnet_start_xmit(struct sk_buff *skb,\n+\t\t\t\t    struct net_device *dev)\n+{\n+\tstruct tbnet *net = netdev_priv(dev);\n+\tstruct tbnet_frame *frames[MAX_SKB_FRAGS];\n+\tu16 frame_id = atomic_read(&net->frame_id);\n+\tstruct thunderbolt_ip_frame_header *hdr;\n+\tunsigned int len = skb_headlen(skb);\n+\tunsigned int data_len = skb->len;\n+\tunsigned int nframes, i;\n+\tunsigned int frag = 0;\n+\tvoid *src = skb->data;\n+\tu32 frame_index = 0;\n+\tbool unmap = false;\n+\tvoid *dest;\n+\n+\tnframes = DIV_ROUND_UP(data_len, TBNET_MAX_PAYLOAD_SIZE);\n+\tif (tbnet_available_buffers(&net->tx_ring) < nframes) {\n+\t\tnetif_stop_queue(net->dev);\n+\t\treturn NETDEV_TX_BUSY;\n+\t}\n+\n+\tframes[frame_index] = tbnet_get_tx_buffer(net);\n+\tif (!frames[frame_index])\n+\t\tgoto err_drop;\n+\n+\thdr = page_address(frames[frame_index]->page);\n+\tdest = hdr + 1;\n+\n+\t/* If overall packet is bigger than the frame data size */\n+\twhile (data_len > TBNET_MAX_PAYLOAD_SIZE) {\n+\t\tunsigned int size_left = TBNET_MAX_PAYLOAD_SIZE;\n+\n+\t\thdr->frame_size = cpu_to_le32(TBNET_MAX_PAYLOAD_SIZE);\n+\t\thdr->frame_index = cpu_to_le16(frame_index);\n+\t\thdr->frame_id = cpu_to_le16(frame_id);\n+\n+\t\tdo {\n+\t\t\tif (len > size_left) {\n+\t\t\t\t/*\n+\t\t\t\t * Copy data onto Tx buffer data with\n+\t\t\t\t * full frame size then break and go to\n+\t\t\t\t * next frame\n+\t\t\t\t */\n+\t\t\t\tmemcpy(dest, src, size_left);\n+\t\t\t\tlen -= size_left;\n+\t\t\t\tdest += size_left;\n+\t\t\t\tsrc += size_left;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tmemcpy(dest, src, len);\n+\t\t\tsize_left -= len;\n+\t\t\tdest += len;\n+\n+\t\t\tif (unmap) {\n+\t\t\t\tkunmap_atomic(src);\n+\t\t\t\tunmap = false;\n+\t\t\t}\n+\n+\t\t\t/* Ensure all fragments have been processed */\n+\t\t\tif (frag < skb_shinfo(skb)->nr_frags) {\n+\t\t\t\t/* Map and then unmap quickly */\n+\t\t\t\tsrc = tbnet_kmap_frag(skb, frag++, &len);\n+\t\t\t\tunmap = true;\n+\t\t\t} else if (unlikely(size_left > 0)) {\n+\t\t\t\tgoto err_drop;\n+\t\t\t}\n+\t\t} while (size_left > 0);\n+\n+\t\tdata_len -= TBNET_MAX_PAYLOAD_SIZE;\n+\t\tframe_index++;\n+\n+\t\tframes[frame_index] = tbnet_get_tx_buffer(net);\n+\t\tif (!frames[frame_index])\n+\t\t\tgoto err_drop;\n+\n+\t\thdr = page_address(frames[frame_index]->page);\n+\t\tdest = hdr + 1;\n+\t}\n+\n+\thdr->frame_size = cpu_to_le32(data_len);\n+\thdr->frame_index = cpu_to_le16(frame_index);\n+\thdr->frame_id = cpu_to_le16(frame_id);\n+\n+\tframes[frame_index]->frame.size = data_len + sizeof(*hdr);\n+\n+\t/* In case  the remaining data_len is smaller than a frame */\n+\twhile (len < data_len) {\n+\t\tmemcpy(dest, src, len);\n+\t\tdata_len -= len;\n+\t\tdest += len;\n+\n+\t\tif (unmap) {\n+\t\t\tkunmap_atomic(src);\n+\t\t\tunmap = false;\n+\t\t}\n+\n+\t\tif (frag < skb_shinfo(skb)->nr_frags) {\n+\t\t\tsrc = tbnet_kmap_frag(skb, frag++, &len);\n+\t\t\tunmap = true;\n+\t\t} else if (unlikely(data_len > 0)) {\n+\t\t\tgoto err_drop;\n+\t\t}\n+\t}\n+\n+\tmemcpy(dest, src, data_len);\n+\n+\tif (unmap)\n+\t\tkunmap_atomic(src);\n+\n+\tif (!tbnet_xmit_csum_and_map(net, skb, frames, frame_index + 1))\n+\t\tgoto err_drop;\n+\n+\tfor (i = 0; i < frame_index + 1; i++)\n+\t\ttb_ring_tx(net->tx_ring.ring, &frames[i]->frame);\n+\n+\tif (net->svc->prtcstns & TBNET_MATCH_FRAGS_ID)\n+\t\tatomic_inc(&net->frame_id);\n+\n+\tnet->stats.tx_packets++;\n+\tnet->stats.tx_bytes += skb->len;\n+\n+\tdev_consume_skb_any(skb);\n+\n+\treturn NETDEV_TX_OK;\n+\n+err_drop:\n+\t/* We can re-use the buffers */\n+\tnet->tx_ring.cons -= frame_index;\n+\n+\tdev_kfree_skb_any(skb);\n+\tnet->stats.tx_errors++;\n+\n+\treturn NETDEV_TX_OK;\n+}\n+\n+static void tbnet_get_stats64(struct net_device *dev,\n+\t\t\t      struct rtnl_link_stats64 *stats)\n+{\n+\tstruct tbnet *net = netdev_priv(dev);\n+\n+\tstats->tx_packets = net->stats.tx_packets;\n+\tstats->rx_packets = net->stats.rx_packets;\n+\tstats->tx_bytes = net->stats.tx_bytes;\n+\tstats->rx_bytes = net->stats.rx_bytes;\n+\tstats->tx_errors = net->stats.tx_errors;\n+\tstats->rx_errors = net->stats.rx_length_errors +\n+\t\tnet->stats.rx_over_errors + net->stats.rx_crc_errors +\n+\t\tnet->stats.rx_missed_errors;\n+\tstats->rx_length_errors = net->stats.rx_length_errors;\n+\tstats->rx_over_errors = net->stats.rx_over_errors;\n+\tstats->rx_crc_errors = net->stats.rx_crc_errors;\n+\tstats->rx_missed_errors = net->stats.rx_missed_errors;\n+}\n+\n+static const struct net_device_ops tbnet_netdev_ops = {\n+\t.ndo_open = tbnet_open,\n+\t.ndo_stop = tbnet_stop,\n+\t.ndo_start_xmit = tbnet_start_xmit,\n+\t.ndo_get_stats64 = tbnet_get_stats64,\n+};\n+\n+static void tbnet_generate_mac(struct net_device *dev)\n+{\n+\tconst struct tbnet *net = netdev_priv(dev);\n+\tconst struct tb_xdomain *xd = net->xd;\n+\tu8 phy_port;\n+\tu32 hash;\n+\n+\tphy_port = tb_phy_port_from_link(TBNET_L0_PORT_NUM(xd->route));\n+\n+\t/* Unicast and locally administered MAC */\n+\tdev->dev_addr[0] = phy_port << 4 | 0x02;\n+\thash = jhash2((u32 *)xd->local_uuid, 4, 0);\n+\tmemcpy(dev->dev_addr + 1, &hash, sizeof(hash));\n+\thash = jhash2((u32 *)xd->local_uuid, 4, hash);\n+\tdev->dev_addr[5] = hash & 0xff;\n+}\n+\n+static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id)\n+{\n+\tstruct tb_xdomain *xd = tb_service_parent(svc);\n+\tstruct net_device *dev;\n+\tstruct tbnet *net;\n+\tint ret;\n+\n+\tdev = alloc_etherdev(sizeof(*net));\n+\tif (!dev)\n+\t\treturn -ENOMEM;\n+\n+\tSET_NETDEV_DEV(dev, &svc->dev);\n+\n+\tnet = netdev_priv(dev);\n+\tINIT_DELAYED_WORK(&net->login_work, tbnet_login_work);\n+\tINIT_WORK(&net->connected_work, tbnet_connected_work);\n+\tmutex_init(&net->connection_lock);\n+\tatomic_set(&net->command_id, 0);\n+\tatomic_set(&net->frame_id, 0);\n+\tnet->svc = svc;\n+\tnet->dev = dev;\n+\tnet->xd = xd;\n+\n+\ttbnet_generate_mac(dev);\n+\n+\tstrcpy(dev->name, \"thunderbolt%d\");\n+\tdev->netdev_ops = &tbnet_netdev_ops;\n+\n+\t/*\n+\t * ThunderboltIP takes advantage of TSO packets but instead of\n+\t * segmenting them we just split the packet into Thunderbolt\n+\t * frames (maximum payload size of each frame is 4084 bytes) and\n+\t * calculate checksum over the whole packet here.\n+\t *\n+\t * The receiving side does the opposite if the host OS supports\n+\t * LRO, otherwise it needs to split the large packet into MTU\n+\t * sized smaller packets.\n+\t *\n+\t * In order to receive large packets from the networking stack,\n+\t * we need to announce support for most of the offloading\n+\t * features here.\n+\t */\n+\tdev->hw_features = NETIF_F_SG | NETIF_F_ALL_TSO | NETIF_F_GRO |\n+\t\t\t   NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;\n+\tdev->features = dev->hw_features | NETIF_F_HIGHDMA;\n+\tdev->hard_header_len += sizeof(struct thunderbolt_ip_frame_header);\n+\n+\tnetif_napi_add(dev, &net->napi, tbnet_poll, NAPI_POLL_WEIGHT);\n+\n+\t/* MTU range: 68 - 65522 */\n+\tdev->min_mtu = ETH_MIN_MTU;\n+\tdev->max_mtu = TBNET_MAX_MTU - ETH_HLEN;\n+\n+\tret = register_netdev(dev);\n+\tif (ret) {\n+\t\tfree_netdev(dev);\n+\t\treturn ret;\n+\t}\n+\n+\tnet->handler.uuid = &tbnet_svc_uuid;\n+\tnet->handler.callback = tbnet_handle_packet,\n+\tnet->handler.data = net;\n+\ttb_register_protocol_handler(&net->handler);\n+\n+\ttb_service_set_drvdata(svc, net);\n+\n+\treturn 0;\n+}\n+\n+static void tbnet_remove(struct tb_service *svc)\n+{\n+\tstruct tbnet *net = tb_service_get_drvdata(svc);\n+\n+\tunregister_netdev(net->dev);\n+\ttb_unregister_protocol_handler(&net->handler);\n+\tfree_netdev(net->dev);\n+}\n+\n+static void tbnet_shutdown(struct tb_service *svc)\n+{\n+\ttbnet_tear_down(tb_service_get_drvdata(svc), true);\n+}\n+\n+static int __maybe_unused tbnet_suspend(struct device *dev)\n+{\n+\tstruct tb_service *svc = tb_to_service(dev);\n+\tstruct tbnet *net = tb_service_get_drvdata(svc);\n+\n+\tstop_login(net);\n+\tif (netif_running(net->dev)) {\n+\t\tnetif_device_detach(net->dev);\n+\t\ttb_ring_stop(net->rx_ring.ring);\n+\t\ttb_ring_stop(net->tx_ring.ring);\n+\t\ttbnet_free_buffers(&net->rx_ring);\n+\t\ttbnet_free_buffers(&net->tx_ring);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int __maybe_unused tbnet_resume(struct device *dev)\n+{\n+\tstruct tb_service *svc = tb_to_service(dev);\n+\tstruct tbnet *net = tb_service_get_drvdata(svc);\n+\n+\tnetif_carrier_off(net->dev);\n+\tif (netif_running(net->dev)) {\n+\t\tnetif_device_attach(net->dev);\n+\t\tstart_login(net);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static const struct dev_pm_ops tbnet_pm_ops = {\n+\tSET_SYSTEM_SLEEP_PM_OPS(tbnet_suspend, tbnet_resume)\n+};\n+\n+static const struct tb_service_id tbnet_ids[] = {\n+\t{ TB_SERVICE(\"network\", 1) },\n+\t{ },\n+};\n+MODULE_DEVICE_TABLE(tbsvc, tbnet_ids);\n+\n+static struct tb_service_driver tbnet_driver = {\n+\t.driver = {\n+\t\t.owner = THIS_MODULE,\n+\t\t.name = \"thunderbolt-net\",\n+\t\t.pm = &tbnet_pm_ops,\n+\t},\n+\t.probe = tbnet_probe,\n+\t.remove = tbnet_remove,\n+\t.shutdown = tbnet_shutdown,\n+\t.id_table = tbnet_ids,\n+};\n+\n+static int __init tbnet_init(void)\n+{\n+\tint ret;\n+\n+\ttbnet_dir = tb_property_create_dir(&tbnet_dir_uuid);\n+\tif (!tbnet_dir)\n+\t\treturn -ENOMEM;\n+\n+\ttb_property_add_immediate(tbnet_dir, \"prtcid\", 1);\n+\ttb_property_add_immediate(tbnet_dir, \"prtcvers\", 1);\n+\ttb_property_add_immediate(tbnet_dir, \"prtcrevs\", 1);\n+\ttb_property_add_immediate(tbnet_dir, \"prtcstns\",\n+\t\t\t\t  TBNET_MATCH_FRAGS_ID);\n+\n+\tret = tb_register_property_dir(\"network\", tbnet_dir);\n+\tif (ret) {\n+\t\ttb_property_free_dir(tbnet_dir);\n+\t\treturn ret;\n+\t}\n+\n+\treturn tb_register_service_driver(&tbnet_driver);\n+}\n+module_init(tbnet_init);\n+\n+static void __exit tbnet_exit(void)\n+{\n+\ttb_unregister_service_driver(&tbnet_driver);\n+\ttb_unregister_property_dir(\"network\", tbnet_dir);\n+\ttb_property_free_dir(tbnet_dir);\n+}\n+module_exit(tbnet_exit);\n+\n+MODULE_AUTHOR(\"Amir Levy <amir.jer.levy@intel.com>\");\n+MODULE_AUTHOR(\"Michael Jamet <michael.jamet@intel.com>\");\n+MODULE_AUTHOR(\"Mika Westerberg <mika.westerberg@linux.intel.com>\");\n+MODULE_DESCRIPTION(\"Thunderbolt networking driver\");\n+MODULE_LICENSE(\"GPL v2\");\n",
    "prefixes": [
        "16/16"
    ]
}