get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2175225,
    "url": "http://patchwork.ozlabs.org/api/1.0/patches/2175225/?format=api",
    "project": {
        "id": 28,
        "url": "http://patchwork.ozlabs.org/api/1.0/projects/28/?format=api",
        "name": "Linux PCI development",
        "link_name": "linux-pci",
        "list_id": "linux-pci.vger.kernel.org",
        "list_email": "linux-pci@vger.kernel.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null
    },
    "msgid": "<20251217151609.3162665-27-den@valinux.co.jp>",
    "date": "2025-12-17T15:16:00",
    "name": "[RFC,v3,26/35] NTB: ntb_transport: Introduce DW eDMA backed transport mode",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "7cd0d21662246140f55cdf840d6d18443cabc40d",
    "submitter": {
        "id": 91573,
        "url": "http://patchwork.ozlabs.org/api/1.0/people/91573/?format=api",
        "name": "Koichiro Den",
        "email": "den@valinux.co.jp"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/linux-pci/patch/20251217151609.3162665-27-den@valinux.co.jp/mbox/",
    "series": [
        {
            "id": 485709,
            "url": "http://patchwork.ozlabs.org/api/1.0/series/485709/?format=api",
            "date": "2025-12-17T15:15:53",
            "name": "NTB transport backed by endpoint DW eDMA",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/485709/mbox/"
        }
    ],
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2175225/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "\n <linux-pci+bounces-43200-incoming=patchwork.ozlabs.org@vger.kernel.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "linux-pci@vger.kernel.org"
        ],
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=valinux.co.jp header.i=@valinux.co.jp\n header.a=rsa-sha256 header.s=selector1 header.b=CidfA6cW;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.105.105.114; helo=tor.lore.kernel.org;\n envelope-from=linux-pci+bounces-43200-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)",
            "smtp.subspace.kernel.org;\n\tdkim=pass (1024-bit key) header.d=valinux.co.jp header.i=@valinux.co.jp\n header.b=\"CidfA6cW\"",
            "smtp.subspace.kernel.org;\n arc=fail smtp.client-ip=52.101.228.20",
            "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=valinux.co.jp",
            "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=valinux.co.jp",
            "dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=valinux.co.jp;"
        ],
        "Received": [
            "from tor.lore.kernel.org (tor.lore.kernel.org [172.105.105.114])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4dWdYT1Mdgz1xty\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 18 Dec 2025 02:50:37 +1100 (AEDT)",
            "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id AD60730B21EF\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 17 Dec 2025 15:26:10 +0000 (UTC)",
            "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 27D6A36C0BA;\n\tWed, 17 Dec 2025 15:18:01 +0000 (UTC)",
            "from OS0P286CU011.outbound.protection.outlook.com\n (mail-japanwestazon11010020.outbound.protection.outlook.com [52.101.228.20])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id C9162369987;\n\tWed, 17 Dec 2025 15:17:56 +0000 (UTC)",
            "from TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:24c::11)\n by TYCP286MB2863.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:306::14) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9434.6; Wed, 17 Dec\n 2025 15:16:37 +0000",
            "from TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM\n ([fe80::fb7e:f4ed:a580:9d03]) by TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM\n ([fe80::fb7e:f4ed:a580:9d03%5]) with mapi id 15.20.9434.001; Wed, 17 Dec 2025\n 15:16:37 +0000"
        ],
        "ARC-Seal": [
            "i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1765984681; cv=fail;\n b=EtyuEJ7p5VIBAfDk+Z5pohqtdboOcj2OTTsxHx0fl+/qSSLt4k5gvslUN1PMnnW1Wiv0ZO1CWNUad91s7+vi3gZp9hFb9y6yB9upmkTFiPGGN/4781hAqObBSPjT1XEcOXZJK22bsq9upjOiF6z468vvfQYLmJFV+gGgII+Mf94=",
            "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=LO1bWkoFS811cf712X/m/FWTRZVfv+qgpbhc7cz4QPeeMds2tY/Z0XRJy8/rI5I1nW/WeKR1gcEjEBWiNsOYR6BxUGWGyUX6cJUQcoB+nGlUcv8liCH8vpZvW5CggWgNpVVzY3E/lV4Oza0CYToz39L8ySMIe27KfBqSYEJKZtSDORUvwrCXvoMRIPdmwymZZjHBLAqmykriqpnyLcV7HMOhCdS5GZh7Bly807fJ6zZN24SdaV1ei6A6M/cBYmm0izHi/Di1McncTUdRs9pWJro3uZ3jtnsdekrqmLPiS6VwrVIpzH6rstNq/LwVihgQlcPXF6jWDDI7qfCANtuecQ=="
        ],
        "ARC-Message-Signature": [
            "i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1765984681; c=relaxed/simple;\n\tbh=g9w1J7/ZEjbSLI0H9IYjPXrn9NLNX10s8CgJG6cB384=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t Content-Type:MIME-Version;\n b=KM/8PBDLSAY4lRYpJ4m+/4SS1HxAovsM7V1gWVqnDleen/Gq/1jNSJqE/DugrmUw9bE+zLozw6FFJtPNaAhsjG0XDXyegDJ6t6aDMtKHcSBnUXVvS8KIUKuQ101Vl1pMNdsgTvu0Eh0+3bfUdb4YYjcPxwmrLxrySIa3c3zqa2Q=",
            "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=upbR1KCMeIs2DM48PtwJNwJBzPLA5l4dOeMx6TDWdPE=;\n b=Y0T6AITeLRLeLd4HTWYowNxVDgiJXOitjmqSq5sgwE7v3H/JA77YY9kSASEUgakGJoaN6D7SBAzJHQsuZw/hndj1e9rasaVxixdRAQtBVXGMZ6Sscu3+f+K6YGqAEZ3rmV+S1ZiRndFCWCNoHjdRgndKWOJVyKnMJmn6ZR3HBdAgh5iVQ/DTleZtPYCJaK7uY9Cm4m+n6oMdJi0yHpcVd0iNLlfhyuk+c9QJVH1xPMjKHFF5hRtzlF47zVAWWA4AAJVNcv8v9K9NktYcfiLDzu8xZfVi6R3HcI/fshkSXF+isbYyOuc+TJxQKS+NsDcnRmfLFMPM/UcgTMG2kmdn/w=="
        ],
        "ARC-Authentication-Results": [
            "i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=valinux.co.jp;\n spf=pass smtp.mailfrom=valinux.co.jp;\n dkim=pass (1024-bit key) header.d=valinux.co.jp header.i=@valinux.co.jp\n header.b=CidfA6cW; arc=fail smtp.client-ip=52.101.228.20",
            "i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=valinux.co.jp; dmarc=pass action=none\n header.from=valinux.co.jp; dkim=pass header.d=valinux.co.jp; arc=none"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=valinux.co.jp;\n s=selector1;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=upbR1KCMeIs2DM48PtwJNwJBzPLA5l4dOeMx6TDWdPE=;\n b=CidfA6cWGQYxiGWh5KiHiqk1Qck3rb21OmSV8USh+laNfN5PYqFwlvFDhchgJKJms1lRevsBgqwunXTx2PTvntfOuSWYWoWPp+sMn5c52hpmbMqfSjw+AAP04EpassyQ9QuinT3xwH+qRPRDiY4EHDWVAGz4z1F6YSzAgD/sA1w=",
        "From": "Koichiro Den <den@valinux.co.jp>",
        "To": "Frank.Li@nxp.com,\n\tdave.jiang@intel.com,\n\tntb@lists.linux.dev,\n\tlinux-pci@vger.kernel.org,\n\tdmaengine@vger.kernel.org,\n\tlinux-renesas-soc@vger.kernel.org,\n\tnetdev@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org",
        "Cc": "mani@kernel.org,\n\tkwilczynski@kernel.org,\n\tkishon@kernel.org,\n\tbhelgaas@google.com,\n\tcorbet@lwn.net,\n\tgeert+renesas@glider.be,\n\tmagnus.damm@gmail.com,\n\trobh@kernel.org,\n\tkrzk+dt@kernel.org,\n\tconor+dt@kernel.org,\n\tvkoul@kernel.org,\n\tjoro@8bytes.org,\n\twill@kernel.org,\n\trobin.murphy@arm.com,\n\tjdmason@kudzu.us,\n\tallenbh@gmail.com,\n\tandrew+netdev@lunn.ch,\n\tdavem@davemloft.net,\n\tedumazet@google.com,\n\tkuba@kernel.org,\n\tpabeni@redhat.com,\n\tBasavaraj.Natikar@amd.com,\n\tShyam-sundar.S-k@amd.com,\n\tkurt.schwemmer@microsemi.com,\n\tlogang@deltatee.com,\n\tjingoohan1@gmail.com,\n\tlpieralisi@kernel.org,\n\tutkarsh02t@gmail.com,\n\tjbrunet@baylibre.com,\n\tdlemoal@kernel.org,\n\tarnd@arndb.de,\n\telfring@users.sourceforge.net,\n\tden@valinux.co.jp",
        "Subject": "[RFC PATCH v3 26/35] NTB: ntb_transport: Introduce DW eDMA backed\n transport mode",
        "Date": "Thu, 18 Dec 2025 00:16:00 +0900",
        "Message-ID": "<20251217151609.3162665-27-den@valinux.co.jp>",
        "X-Mailer": "git-send-email 2.51.0",
        "In-Reply-To": "<20251217151609.3162665-1-den@valinux.co.jp>",
        "References": "<20251217151609.3162665-1-den@valinux.co.jp>",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-ClientProxiedBy": "TYWP286CA0030.JPNP286.PROD.OUTLOOK.COM\n (2603:1096:400:262::20) To TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM\n (2603:1096:400:24c::11)",
        "Precedence": "bulk",
        "X-Mailing-List": "linux-pci@vger.kernel.org",
        "List-Id": "<linux-pci.vger.kernel.org>",
        "List-Subscribe": "<mailto:linux-pci+subscribe@vger.kernel.org>",
        "List-Unsubscribe": "<mailto:linux-pci+unsubscribe@vger.kernel.org>",
        "MIME-Version": "1.0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "TYWP286MB2697:EE_|TYCP286MB2863:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "aaf6cf86-2274-4725-ce24-08de3d7f459e",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "\n\tBCL:0;ARA:13230040|7416014|376014|10070799003|1800799024|366016;",
        "X-Microsoft-Antispam-Message-Info": "\n S3V7jzYr6nRDnjL6JW1+NGOSLeBVX1AVUkZR7JAn5soFdtTzhABH6/SzWWEGcsxVQrKuYKaSeTmpJfGxf0K6QTAg7gas45QT7RjK4JeZv2g+h9AdTZNxOWVWnc/R1YlrjHhph5Awth7qi4eFs5aCBpj7fdSMU3sGcfzd8JxjHeW/G7WsnV+nft2K8n4q92BJxDQOlwFYw+40q5Zxe5JefYW2S0Cddi6XHfXq9BTQAZ0d4DjlLb+08pl+S+3byHq60tNWaYApNrMLFaQ8nID/lrltM0NSiDQAcfwjwa0PO7VDLylCUVwuIedQ/99ua+bQ+aiJLgIZFn1l0StnIC0hxnuW96ypOz1C3cDu30nDPJd9/30ag7AnLid8R5qExH2xOBJ8yoCfNZKKVeWyzUUP7zMsrle80s74zRQClNyay2c8sGzt50W0tDYZ4bHwhvz/tz9IVBrkhZz56R2wu4fimjQTtdTXoAb2d+Pkqcg5WEc7+q1pPPzeo+8aztIqvHDK94YOz8JgRWX6HpkSmy1czjQqFCV+1xtvFheOv1cUtoORT6MvWw1jZZQqWZMBmMqXKa5LvLuyJPqRqaT/6eAOp3pdiVAXD76zIvd/TdkHacDCEtWOwev1onEmeb0lCUq/Gz4YOWXP/7cp3PrmszAKexrGSPMOR5KEj+QyI3ABCJ9m2UzmvDRy57tfbnfW5+TgzWL79QGHq6RL00ShdAQb+oEqXqlzvdCYLLAUpofbla/N+2dG0Nckb27aD2ldxLcixaJmowZJq6aWEfY2mVy0KAhLfpmhUpU6pHsVAtpsgjly148Iuua3nDYjNeaEbB9q2Ex9Ij3qrZugC9VYzeszWnKAdvBQaF4S16Z3lVSLfTWghwRbkNkIuWjd1U+cKN8cqwBnBvYZHrs9xNUhACWXi0jEL00+mmVHO8LjS/ORFlrua+Q87KyQcGKfRhS9SVM3t9x++6iTn8lhV/f/GG0AbvT/h1V3JusYNGhCsR+nrikj6/x0mND5WTcMvhdnWcj3w+qLkdkipd5X7Zsbtea3Ukyme4xogAeleOV6X+bbF8m+ogeS8SVNQA4oqYjKchb5xsE6GrhpWKWXiSknc+tdRDmPGkFuHZFM6hDOi8KYvWongAIYBs5f13cd8hORVvVDog5oIdiqJTYobXO2xNgaswKLRUoubH7J6XdeQB7F19scyDTjVN9uISnzIywKr0Z85W1SYzcstDD7ODwAI0Lkqe9BiU1kL7N3i+JMSKVTmstkla8l7mreJ5YX80vkZp5w+0RgZRLesJADphja2kmaXY2Ju0bHlRZucKl0dz5ixi8Qv0sPDaukvCrAVCrbcXBT4SaD2U7vJts38A+NtmfpZBBA1CmrdAKRAS3w3oJ9s87Tv6cT5vgh3ODgJ+NKvwNZiwOiqgk47vi1F698ZjLJTlTOK60emW8Qxcrd0yz8IIJA+A5S4HMZk/lbojyYp5MK",
        "X-Forefront-Antispam-Report": "\n\tCIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(7416014)(376014)(10070799003)(1800799024)(366016);DIR:OUT;SFP:1101;",
        "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-MessageData-0": "\n 7ncvDeaMNkcp7cjLbFD/BI3+BLmAwzTijZ3Tb4cyGvH4B3b4hdVBLfvJPB8m9rNXM+yezT5y6j17mPh3cfVZQ3AqMptSjlE2gF/LO+J7BtkfGr1XppLHBjq0yfmIUCojW4M1freKEa08gkdA1E1kX5K+NIjewLHahNi9LLdZ8r6+Ua+em6bnlIX/07JRQGryGCdWH3KozZUDm0Rb9hUrilqOkLhUlieCA9pxvpJGjqpppUtAgW9jwMHhZGw1XIxnqNQt+q6AsKu0ZIt5wRvnIjXvnLLU+XqZW8kPDFm3NlGP2EXL5e6U9fjjjIhgBI5PmMKuygNc9mOm+vwdY9BVMcCUgSQNJOCfRc6k2CMgqX6j6I/RxkxnSsEf+AJG60zbDIc0DHJFRZA0LjNlS+YpiUc+YQ9vWYAScMhYHm8/dNIqZWGHlS3L0pm18GDCKT5Tg23yd2DytzNE8lp0sMmUDH4ZLi3mMnt6RJTLtnJTf06jVsnSuAxuRXTLnZ7dLXSGVuS0amWxs0vxuWATDsUx/sCSAsqfYwzb+OtIXssqUyrlTTVqA3vBRqaZZEnT63ci/kJfyQW9zhmIzHwm4/eKSiNFSQIWmXxhIDD0YazGfWN3hnatvb635mFuhRljVwHVYhQpPepiGxH8v5l9tJHCdHml7zmxG+UySuRpZCGk9anFSuXijSnsZhqJDpjNwyNLQCSVJzB6bgIDqdH11IdCdGeX9HpV5wlt73X3BDaQ6/0I1XlwruHXaoN26gzPfS4AQB9PK/Nm5duDFyCpvLCyxKAE4z0aP/72YgHcO+IjkN8Vn9ydH3OYI7kqAGdwAzWmiZx1G0kO7hG1daAymBCR0myvhjotXjfK0hm9NQyGLhCj8ar6jagWUH9Yeh787OU1YWvBv2kgT8N2netqpvLt90mG7vGIQdr1Dl4abN+0nAJ6J+OyTxJsPjdOQ4fspt7HLtEuWBnMDbjWX7RyJ21gLmeQ5tuMwxLQyZoZLEWP7kVtnmvUj4rTam8vhk/GB2evcE+qwsusJdDJZpAFDmAIL2vzOzXhSJXKC/hEBb+RSBqv4v3zGRFOsLb70RDWg0a0XAeopvjvYLr8kx7SsqKz4L1T8AVBtpaeWsgCH1lVtrNtcdaQ3nBlpxtN/VOWs9ysK7QYkbyKTjlr8HEFVngdt+/GKZbtT+EftFbcclMOlBV0xbfcKOWZRoc/H5hQJ8dOU8zQT3OCHtl5gaDOFW8ROnyUFQ7fluWasqpPlMV5sdTX331jRBtofoLknQ4MoIepzIgW75Eo7HrAszY4tqjZ1P+hBcn9J0WYLPJ2v40e9GLwFA8wSbhY2siCKRsYugKw/ayuoAM6W3fBcCf0fxCC2wVgHNxXITlEJ+uKr0ChlTbzoVZ9PngsrOo2TM7MGUPzAshRv4NaUYhUa8OvwZFQRoa9HaKyVaSxCb6FXhskyom/e8FOxh/Q55BXc3MWwBqmRymYhLJarED5frBf7C+os1s5PeXhI0ZOLKGi+fpTJHiS4MeUOaTkMUqytDqY5TtlCmv7KncyNluFiWhJjD1ugO0p8Q45OOJtGSMC/3IEu8QU0Qo3klx+s9qUMQUVRRtjgZwiKfdxFsdms/BuI2Jv5+Zsx3jJdtiwK7iJnGjqCsc=",
        "X-OriginatorOrg": "valinux.co.jp",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n aaf6cf86-2274-4725-ce24-08de3d7f459e",
        "X-MS-Exchange-CrossTenant-AuthSource": "TYWP286MB2697.JPNP286.PROD.OUTLOOK.COM",
        "X-MS-Exchange-CrossTenant-AuthAs": "Internal",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "17 Dec 2025 15:16:37.2474\n (UTC)",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted",
        "X-MS-Exchange-CrossTenant-Id": "7a57bee8-f73d-4c5f-a4f7-d72c91c8c111",
        "X-MS-Exchange-CrossTenant-MailboxType": "HOSTED",
        "X-MS-Exchange-CrossTenant-UserPrincipalName": "\n 8c8/bQ09WLncXZW6yLHE4ZTCm6nT5JvpYl3H4Ijrd8RmG8FIcAg/Yu4mEkB21/57dhKgyEywVZ3xv+T+1Lca8g==",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "TYCP286MB2863"
    },
    "content": "Add a new ntb_transport backend that uses a DesignWare eDMA engine\nlocated on the endpoint, to be driven by both host and endpoint.\n\nThe endpoint exposes a dedicated memory window which contains the eDMA\nregister block, a small control structure (struct ntb_edma_info) and\nper-channel linked-list (LL) rings for read channels. Endpoint drives\nits local eDMA write channels for its transmission, while host side\nuses the remote eDMA read channels for its transmission.\n\nA key benefit of this backend is that the memory window no longer needs\nto carry data-plane payload. This makes the design less sensitive to\nlimited memory window space and allows scaling to multiple queue pairs.\nThe memory window layout is specific to the eDMA-backed backend, so\nthere is no automatic fallback to the memcpy-based default transport\nthat requires the different layout.\n\nSigned-off-by: Koichiro Den <den@valinux.co.jp>\n---\n drivers/ntb/Kconfig                  |  12 +\n drivers/ntb/Makefile                 |   2 +\n drivers/ntb/ntb_transport_core.c     |  15 +-\n drivers/ntb/ntb_transport_edma.c     | 987 +++++++++++++++++++++++++++\n drivers/ntb/ntb_transport_internal.h |  15 +\n 5 files changed, 1029 insertions(+), 2 deletions(-)\n create mode 100644 drivers/ntb/ntb_transport_edma.c",
    "diff": "diff --git a/drivers/ntb/Kconfig b/drivers/ntb/Kconfig\nindex df16c755b4da..5ba6d0b7f5ba 100644\n--- a/drivers/ntb/Kconfig\n+++ b/drivers/ntb/Kconfig\n@@ -37,4 +37,16 @@ config NTB_TRANSPORT\n \n \t If unsure, say N.\n \n+config NTB_TRANSPORT_EDMA\n+\tbool \"NTB Transport backed by remote eDMA\"\n+\tdepends on NTB_TRANSPORT\n+\tdepends on PCI\n+\tselect DMA_ENGINE\n+\tselect NTB_EDMA\n+\thelp\n+\t  Enable a transport backend that uses a remote DesignWare eDMA engine\n+\t  exposed through a dedicated NTB memory window. The host uses the\n+\t  endpoint's eDMA engine to move data in both directions.\n+\t  Say Y here if you intend to use the 'use_remote_edma' module parameter.\n+\n endif # NTB\ndiff --git a/drivers/ntb/Makefile b/drivers/ntb/Makefile\nindex 9b66e5fafbc0..b9086b32ecde 100644\n--- a/drivers/ntb/Makefile\n+++ b/drivers/ntb/Makefile\n@@ -6,3 +6,5 @@ ntb-y\t\t\t:= core.o\n ntb-$(CONFIG_NTB_MSI)\t+= msi.o\n \n ntb_transport-y\t\t:= ntb_transport_core.o\n+ntb_transport-$(CONFIG_NTB_TRANSPORT_EDMA)\t+= ntb_transport_edma.o\n+ntb_transport-$(CONFIG_NTB_TRANSPORT_EDMA)\t+= hw/edma/ntb_hw_edma.o\ndiff --git a/drivers/ntb/ntb_transport_core.c b/drivers/ntb/ntb_transport_core.c\nindex 40c2548f5930..bd21232f26fe 100644\n--- a/drivers/ntb/ntb_transport_core.c\n+++ b/drivers/ntb/ntb_transport_core.c\n@@ -104,6 +104,12 @@ module_param(use_msi, bool, 0644);\n MODULE_PARM_DESC(use_msi, \"Use MSI interrupts instead of doorbells\");\n #endif\n \n+bool use_remote_edma;\n+#ifdef CONFIG_NTB_TRANSPORT_EDMA\n+module_param(use_remote_edma, bool, 0644);\n+MODULE_PARM_DESC(use_remote_edma, \"Use remote eDMA mode (when enabled, use_msi is ignored)\");\n+#endif\n+\n static struct dentry *nt_debugfs_dir;\n \n /* Only two-ports NTB devices are supported */\n@@ -156,7 +162,7 @@ enum {\n #define drv_client(__drv) \\\n \tcontainer_of((__drv), struct ntb_transport_client, driver)\n \n-#define NTB_QP_DEF_NUM_ENTRIES\t100\n+#define NTB_QP_DEF_NUM_ENTRIES\t128\n #define NTB_LINK_DOWN_TIMEOUT\t10\n \n static void ntb_transport_rxc_db(unsigned long data);\n@@ -1189,7 +1195,11 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)\n \n \tnt->ndev = ndev;\n \n-\trc = ntb_transport_default_init(nt);\n+\tif (use_remote_edma)\n+\t\trc = ntb_transport_edma_init(nt);\n+\telse\n+\t\trc = ntb_transport_default_init(nt);\n+\n \tif (rc)\n \t\treturn rc;\n \n@@ -1950,6 +1960,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev,\n \n \tnt->qp_bitmap_free &= ~qp_bit;\n \n+\tqp->qp_bit = qp_bit;\n \tqp->cb_data = data;\n \tqp->rx_handler = handlers->rx_handler;\n \tqp->tx_handler = handlers->tx_handler;\ndiff --git a/drivers/ntb/ntb_transport_edma.c b/drivers/ntb/ntb_transport_edma.c\nnew file mode 100644\nindex 000000000000..6ae5da0a1367\n--- /dev/null\n+++ b/drivers/ntb/ntb_transport_edma.c\n@@ -0,0 +1,987 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * NTB transport backend for remote DesignWare eDMA.\n+ *\n+ * This implements the backend_ops used when use_remote_edma=1 and\n+ * relies on drivers/ntb/hw/edma/ for low-level eDMA/MW programming.\n+ */\n+\n+#include <linux/bug.h>\n+#include <linux/compiler.h>\n+#include <linux/debugfs.h>\n+#include <linux/dmaengine.h>\n+#include <linux/dma-mapping.h>\n+#include <linux/errno.h>\n+#include <linux/io-64-nonatomic-lo-hi.h>\n+#include <linux/ntb.h>\n+#include <linux/pci.h>\n+#include <linux/pci-epc.h>\n+#include <linux/seq_file.h>\n+#include <linux/slab.h>\n+\n+#include \"hw/edma/ntb_hw_edma.h\"\n+#include \"ntb_transport_internal.h\"\n+\n+#define NTB_EDMA_RING_ORDER\t7\n+#define NTB_EDMA_RING_ENTRIES\t(1U << NTB_EDMA_RING_ORDER)\n+#define NTB_EDMA_RING_MASK\t(NTB_EDMA_RING_ENTRIES - 1)\n+\n+#define NTB_EDMA_MAX_POLL\t32\n+\n+/*\n+ * Remote eDMA mode implementation\n+ */\n+struct ntb_transport_ctx_edma {\n+\tremote_edma_mode_t remote_edma_mode;\n+\tstruct device *dma_dev;\n+\tstruct workqueue_struct *wq;\n+\tstruct ntb_edma_chans chans;\n+};\n+\n+struct ntb_transport_qp_edma {\n+\tstruct ntb_transport_qp *qp;\n+\n+\t/*\n+\t * For ensuring peer notification in non-atomic context.\n+\t * ntb_peer_db_set might sleep or schedule.\n+\t */\n+\tstruct work_struct db_work;\n+\n+\tu32 rx_prod;\n+\tu32 rx_cons;\n+\tu32 tx_cons;\n+\tu32 tx_issue;\n+\n+\tspinlock_t rx_lock;\n+\tspinlock_t tx_lock;\n+\n+\tstruct work_struct rx_work;\n+\tstruct work_struct tx_work;\n+};\n+\n+struct ntb_edma_desc {\n+\tu32 len;\n+\tu32 flags;\n+\tu64 addr; /* DMA address */\n+\tu64 data;\n+};\n+\n+struct ntb_edma_ring {\n+\tstruct ntb_edma_desc desc[NTB_EDMA_RING_ENTRIES];\n+\tu32 head;\n+\tu32 tail;\n+};\n+\n+static inline bool ntb_qp_edma_is_rc(struct ntb_transport_qp *qp)\n+{\n+\tstruct ntb_transport_ctx_edma *ctx = qp->transport->priv;\n+\n+\treturn ctx->remote_edma_mode == REMOTE_EDMA_RC;\n+}\n+\n+static inline bool ntb_qp_edma_is_ep(struct ntb_transport_qp *qp)\n+{\n+\tstruct ntb_transport_ctx_edma *ctx = qp->transport->priv;\n+\n+\treturn ctx->remote_edma_mode == REMOTE_EDMA_EP;\n+}\n+\n+static inline bool ntb_qp_edma_enabled(struct ntb_transport_qp *qp)\n+{\n+\treturn ntb_qp_edma_is_rc(qp) || ntb_qp_edma_is_ep(qp);\n+}\n+\n+static inline unsigned int ntb_edma_ring_sel(struct ntb_transport_qp *qp,\n+\t\t\t\t\t     unsigned int n)\n+{\n+\treturn n ^ !!ntb_qp_edma_is_ep(qp);\n+}\n+\n+static inline struct ntb_edma_ring *\n+ntb_edma_ring_local(struct ntb_transport_qp *qp, unsigned int n)\n+{\n+\tunsigned int r = ntb_edma_ring_sel(qp, n);\n+\n+\treturn &((struct ntb_edma_ring *)qp->rx_buff)[r];\n+}\n+\n+static inline struct ntb_edma_ring __iomem *\n+ntb_edma_ring_remote(struct ntb_transport_qp *qp, unsigned int n)\n+{\n+\tunsigned int r = ntb_edma_ring_sel(qp, n);\n+\n+\treturn &((struct ntb_edma_ring __iomem *)qp->tx_mw)[r];\n+}\n+\n+static inline struct ntb_edma_desc *\n+ntb_edma_desc_local(struct ntb_transport_qp *qp, unsigned int n, unsigned int i)\n+{\n+\treturn &ntb_edma_ring_local(qp, n)->desc[i];\n+}\n+\n+static inline struct ntb_edma_desc __iomem *\n+ntb_edma_desc_remote(struct ntb_transport_qp *qp, unsigned int n,\n+\t\t     unsigned int i)\n+{\n+\treturn &ntb_edma_ring_remote(qp, n)->desc[i];\n+}\n+\n+static inline u32 *ntb_edma_head_local(struct ntb_transport_qp *qp,\n+\t\t\t\t       unsigned int n)\n+{\n+\treturn &ntb_edma_ring_local(qp, n)->head;\n+}\n+\n+static inline u32 __iomem *ntb_edma_head_remote(struct ntb_transport_qp *qp,\n+\t\t\t\t\t\tunsigned int n)\n+{\n+\treturn &ntb_edma_ring_remote(qp, n)->head;\n+}\n+\n+static inline u32 *ntb_edma_tail_local(struct ntb_transport_qp *qp,\n+\t\t\t\t       unsigned int n)\n+{\n+\treturn &ntb_edma_ring_local(qp, n)->tail;\n+}\n+\n+static inline u32 __iomem *ntb_edma_tail_remote(struct ntb_transport_qp *qp,\n+\t\t\t\t\t\tunsigned int n)\n+{\n+\treturn &ntb_edma_ring_remote(qp, n)->tail;\n+}\n+\n+/* The 'i' must be generated by ntb_edma_ring_idx() */\n+#define NTB_DESC_TX_O(qp, i)\tntb_edma_desc_remote(qp, 0, i)\n+#define NTB_DESC_TX_I(qp, i)\tntb_edma_desc_local(qp, 0, i)\n+#define NTB_DESC_RX_O(qp, i)\tntb_edma_desc_remote(qp, 1, i)\n+#define NTB_DESC_RX_I(qp, i)\tntb_edma_desc_local(qp, 1, i)\n+\n+#define NTB_HEAD_TX_I(qp)\tntb_edma_head_local(qp, 0)\n+#define NTB_HEAD_RX_O(qp)\tntb_edma_head_remote(qp, 1)\n+\n+#define NTB_TAIL_TX_O(qp)\tntb_edma_tail_remote(qp, 0)\n+#define NTB_TAIL_RX_I(qp)\tntb_edma_tail_local(qp, 1)\n+\n+/* ntb_edma_ring helpers */\n+static __always_inline u32 ntb_edma_ring_idx(u32 v)\n+{\n+\treturn v & NTB_EDMA_RING_MASK;\n+}\n+\n+static __always_inline u32 ntb_edma_ring_used_entry(u32 head, u32 tail)\n+{\n+\tif (head >= tail) {\n+\t\tWARN_ON_ONCE((head - tail) > (NTB_EDMA_RING_ENTRIES - 1));\n+\t\treturn head - tail;\n+\t}\n+\n+\tWARN_ON_ONCE((U32_MAX - tail + head + 1) > (NTB_EDMA_RING_ENTRIES - 1));\n+\treturn U32_MAX - tail + head + 1;\n+}\n+\n+static __always_inline u32 ntb_edma_ring_free_entry(u32 head, u32 tail)\n+{\n+\treturn NTB_EDMA_RING_ENTRIES - ntb_edma_ring_used_entry(head, tail) - 1;\n+}\n+\n+static __always_inline bool ntb_edma_ring_full(u32 head, u32 tail)\n+{\n+\treturn ntb_edma_ring_free_entry(head, tail) == 0;\n+}\n+\n+static unsigned int ntb_transport_edma_tx_free_entry(struct ntb_transport_qp *qp)\n+{\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\tunsigned int head, tail;\n+\n+\tscoped_guard(spinlock_irqsave, &edma->tx_lock) {\n+\t\t/* In this scope, only 'head' might proceed */\n+\t\ttail = READ_ONCE(edma->tx_issue);\n+\t\thead = READ_ONCE(*NTB_HEAD_TX_I(qp));\n+\t}\n+\t/*\n+\t * 'used' amount indicates how much the other end has refilled,\n+\t * which are available for us to use for TX.\n+\t */\n+\treturn ntb_edma_ring_used_entry(head, tail);\n+}\n+\n+static void ntb_transport_edma_debugfs_stats_show(struct seq_file *s,\n+\t\t\t\t\t\t  struct ntb_transport_qp *qp)\n+{\n+\tseq_printf(s, \"rx_bytes - \\t%llu\\n\", qp->rx_bytes);\n+\tseq_printf(s, \"rx_pkts - \\t%llu\\n\", qp->rx_pkts);\n+\tseq_printf(s, \"rx_err_no_buf - %llu\\n\", qp->rx_err_no_buf);\n+\tseq_printf(s, \"rx_buff - \\t0x%p\\n\", qp->rx_buff);\n+\tseq_printf(s, \"rx_max_entry - \\t%u\\n\", qp->rx_max_entry);\n+\tseq_printf(s, \"rx_alloc_entry - \\t%u\\n\\n\", qp->rx_alloc_entry);\n+\n+\tseq_printf(s, \"tx_bytes - \\t%llu\\n\", qp->tx_bytes);\n+\tseq_printf(s, \"tx_pkts - \\t%llu\\n\", qp->tx_pkts);\n+\tseq_printf(s, \"tx_ring_full - \\t%llu\\n\", qp->tx_ring_full);\n+\tseq_printf(s, \"tx_err_no_buf - %llu\\n\", qp->tx_err_no_buf);\n+\tseq_printf(s, \"tx_mw - \\t0x%p\\n\", qp->tx_mw);\n+\tseq_printf(s, \"tx_max_entry - \\t%u\\n\", qp->tx_max_entry);\n+\tseq_printf(s, \"free tx - \\t%u\\n\", ntb_transport_tx_free_entry(qp));\n+\tseq_putc(s, '\\n');\n+\n+\tseq_puts(s, \"Using Remote eDMA - Yes\\n\");\n+\tseq_printf(s, \"QP Link - \\t%s\\n\", qp->link_is_up ? \"Up\" : \"Down\");\n+}\n+\n+static void ntb_transport_edma_uninit(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\n+\tif (ctx->wq)\n+\t\tdestroy_workqueue(ctx->wq);\n+\tctx->wq = NULL;\n+\n+\tntb_edma_teardown_chans(&ctx->chans);\n+\n+\tswitch (ctx->remote_edma_mode) {\n+\tcase REMOTE_EDMA_EP:\n+\t\tntb_edma_teardown_mws(nt->ndev);\n+\t\tbreak;\n+\tcase REMOTE_EDMA_RC:\n+\t\tntb_edma_teardown_peer(nt->ndev);\n+\t\tbreak;\n+\tcase REMOTE_EDMA_UNKNOWN:\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\tctx->remote_edma_mode = REMOTE_EDMA_UNKNOWN;\n+}\n+\n+static void ntb_transport_edma_db_work(struct work_struct *work)\n+{\n+\tstruct ntb_transport_qp_edma *edma =\n+\t\t\tcontainer_of(work, struct ntb_transport_qp_edma, db_work);\n+\tstruct ntb_transport_qp *qp = edma->qp;\n+\n+\tntb_peer_db_set(qp->ndev, qp->qp_bit);\n+}\n+\n+static void ntb_transport_edma_notify_peer(struct ntb_transport_qp_edma *edma)\n+{\n+\tstruct ntb_transport_qp *qp = edma->qp;\n+\tstruct ntb_transport_ctx_edma *ctx = qp->transport->priv;\n+\n+\tif (!ntb_edma_notify_peer(&ctx->chans, qp->qp_num))\n+\t\treturn;\n+\n+\t/*\n+\t * Called from contexts that may be atomic. Since ntb_peer_db_set()\n+\t * may sleep, delegate the actual doorbell write to a workqueue.\n+\t */\n+\tqueue_work(system_highpri_wq, &edma->db_work);\n+}\n+\n+static void ntb_transport_edma_isr(void *data, int qp_num)\n+{\n+\tstruct ntb_transport_ctx *nt = data;\n+\tstruct ntb_transport_qp_edma *edma;\n+\tstruct ntb_transport_ctx_edma *ctx;\n+\tstruct ntb_transport_qp *qp;\n+\n+\tif (qp_num < 0 || qp_num >= nt->qp_count)\n+\t\treturn;\n+\n+\tqp = &nt->qp_vec[qp_num];\n+\tif (WARN_ON(!qp))\n+\t\treturn;\n+\n+\tctx = (struct ntb_transport_ctx_edma *)qp->transport->priv;\n+\tedma = qp->priv;\n+\n+\tqueue_work(ctx->wq, &edma->rx_work);\n+\tqueue_work(ctx->wq, &edma->tx_work);\n+}\n+\n+static int ntb_transport_edma_rc_init(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct pci_dev *pdev = ndev->pdev;\n+\tint peer_mw;\n+\tint rc;\n+\n+\tif (!use_remote_edma || ctx->remote_edma_mode != REMOTE_EDMA_UNKNOWN)\n+\t\treturn 0;\n+\n+\tpeer_mw = ntb_peer_mw_count(ndev);\n+\tif (peer_mw <= 0)\n+\t\treturn -ENODEV;\n+\n+\trc = ntb_edma_setup_peer(ndev, peer_mw - 1, nt->qp_count);\n+\tif (rc) {\n+\t\tdev_err(&pdev->dev, \"Failed to enable remote eDMA: %d\\n\", rc);\n+\t\treturn rc;\n+\t}\n+\n+\trc = ntb_edma_setup_chans(get_dma_dev(ndev), &ctx->chans, true);\n+\tif (rc) {\n+\t\tdev_err(&pdev->dev, \"Failed to setup eDMA channels: %d\\n\", rc);\n+\t\tgoto err_teardown_peer;\n+\t}\n+\n+\trc = ntb_edma_setup_intr_chan(get_dma_dev(ndev), &ctx->chans);\n+\tif (rc) {\n+\t\tdev_err(&pdev->dev, \"Failed to setup eDMA notify channel: %d\\n\",\n+\t\t\trc);\n+\t\tgoto err_teardown_chans;\n+\t}\n+\n+\tctx->remote_edma_mode = REMOTE_EDMA_RC;\n+\treturn 0;\n+\n+err_teardown_chans:\n+\tntb_edma_teardown_chans(&ctx->chans);\n+err_teardown_peer:\n+\tntb_edma_teardown_peer(ndev);\n+\treturn rc;\n+}\n+\n+\n+static int ntb_transport_edma_ep_init(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct pci_dev *pdev = ndev->pdev;\n+\tint peer_mw;\n+\tint rc;\n+\n+\tif (!use_remote_edma || ctx->remote_edma_mode == REMOTE_EDMA_EP)\n+\t\treturn 0;\n+\n+\t/**\n+\t * This check assumes that the endpoint (pci-epf-vntb.c)\n+\t * ntb_dev_ops implements .get_private_data() while the host side\n+\t * (ntb_hw_epf.c) does not.\n+\t */\n+\tif (!ntb_get_private_data(ndev))\n+\t\treturn 0;\n+\n+\tpeer_mw = ntb_peer_mw_count(ndev);\n+\tif (peer_mw <= 0)\n+\t\treturn -ENODEV;\n+\n+\trc = ntb_edma_setup_mws(ndev, peer_mw - 1, nt->qp_count,\n+\t\t\t\tntb_transport_edma_isr, nt);\n+\tif (rc) {\n+\t\tdev_err(&pdev->dev,\n+\t\t\t\"Failed to set up memory window for eDMA: %d\\n\", rc);\n+\t\treturn rc;\n+\t}\n+\n+\trc = ntb_edma_setup_chans(get_dma_dev(ndev), &ctx->chans, false);\n+\tif (rc) {\n+\t\tdev_err(&pdev->dev, \"Failed to setup eDMA channels: %d\\n\", rc);\n+\t\tntb_edma_teardown_mws(ndev);\n+\t\treturn rc;\n+\t}\n+\n+\tctx->remote_edma_mode = REMOTE_EDMA_EP;\n+\treturn 0;\n+}\n+\n+\n+static int ntb_transport_edma_setup_qp_mw(struct ntb_transport_ctx *nt,\n+\t\t\t\t\t  unsigned int qp_num)\n+{\n+\tstruct ntb_transport_qp *qp = &nt->qp_vec[qp_num];\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct ntb_queue_entry *entry;\n+\tstruct ntb_transport_mw *mw;\n+\tunsigned int mw_num, mw_count, qp_count;\n+\tunsigned int qp_offset, rx_info_offset;\n+\tunsigned int mw_size, mw_size_per_qp;\n+\tunsigned int num_qps_mw;\n+\tsize_t edma_total;\n+\tunsigned int i;\n+\tint node;\n+\n+\tmw_count = nt->mw_count;\n+\tqp_count = nt->qp_count;\n+\n+\tmw_num = QP_TO_MW(nt, qp_num);\n+\tmw = &nt->mw_vec[mw_num];\n+\n+\tif (!mw->virt_addr)\n+\t\treturn -ENOMEM;\n+\n+\tif (mw_num < qp_count % mw_count)\n+\t\tnum_qps_mw = qp_count / mw_count + 1;\n+\telse\n+\t\tnum_qps_mw = qp_count / mw_count;\n+\n+\tmw_size = min(nt->mw_vec[mw_num].phys_size, mw->xlat_size);\n+\tif (max_mw_size && mw_size > max_mw_size)\n+\t\tmw_size = max_mw_size;\n+\n+\tmw_size_per_qp = round_down((unsigned int)mw_size / num_qps_mw, SZ_64);\n+\tqp_offset = mw_size_per_qp * (qp_num / mw_count);\n+\trx_info_offset = mw_size_per_qp - sizeof(struct ntb_rx_info);\n+\n+\tqp->tx_mw_size = mw_size_per_qp;\n+\tqp->tx_mw = nt->mw_vec[mw_num].vbase + qp_offset;\n+\tif (!qp->tx_mw)\n+\t\treturn -EINVAL;\n+\tqp->tx_mw_phys = nt->mw_vec[mw_num].phys_addr + qp_offset;\n+\tif (!qp->tx_mw_phys)\n+\t\treturn -EINVAL;\n+\tqp->rx_info = qp->tx_mw + rx_info_offset;\n+\tqp->rx_buff = mw->virt_addr + qp_offset;\n+\tqp->remote_rx_info = qp->rx_buff + rx_info_offset;\n+\n+\t/* Due to housekeeping, there must be at least 2 buffs */\n+\tqp->tx_max_frame = min(transport_mtu, mw_size_per_qp / 2);\n+\tqp->rx_max_frame = min(transport_mtu, mw_size_per_qp / 2);\n+\n+\t/* In eDMA mode, decouple from MW sizing and force ring-sized entries */\n+\tedma_total = 2 * sizeof(struct ntb_edma_ring);\n+\tif (rx_info_offset < edma_total) {\n+\t\tdev_err(&ndev->dev, \"Ring space requires %zuB (>=%uB)\\n\",\n+\t\t\tedma_total, rx_info_offset);\n+\t\treturn -EINVAL;\n+\t}\n+\tqp->tx_max_entry = NTB_EDMA_RING_ENTRIES;\n+\tqp->rx_max_entry = NTB_EDMA_RING_ENTRIES;\n+\n+\t/*\n+\t * Checking to see if we have more entries than the default.\n+\t * We should add additional entries if that is the case so we\n+\t * can be in sync with the transport frames.\n+\t */\n+\tnode = dev_to_node(&ndev->dev);\n+\tfor (i = qp->rx_alloc_entry; i < qp->rx_max_entry; i++) {\n+\t\tentry = kzalloc_node(sizeof(*entry), GFP_KERNEL, node);\n+\t\tif (!entry)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tentry->qp = qp;\n+\t\tntb_list_add(&qp->ntb_rx_q_lock, &entry->entry,\n+\t\t\t     &qp->rx_free_q);\n+\t\tqp->rx_alloc_entry++;\n+\t}\n+\n+\tmemset(qp->rx_buff, 0, edma_total);\n+\n+\tqp->rx_pkts = 0;\n+\tqp->tx_pkts = 0;\n+\n+\treturn 0;\n+}\n+\n+static int ntb_transport_edma_rx_complete(struct ntb_transport_qp *qp)\n+{\n+\tstruct device *dma_dev = get_dma_dev(qp->ndev);\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\tstruct ntb_queue_entry *entry;\n+\tstruct ntb_edma_desc *in;\n+\tunsigned int len;\n+\tbool link_down;\n+\tu32 idx;\n+\n+\tif (ntb_edma_ring_used_entry(READ_ONCE(*NTB_TAIL_RX_I(qp)),\n+\t\t\t\t     edma->rx_cons) == 0)\n+\t\treturn 0;\n+\n+\tidx = ntb_edma_ring_idx(edma->rx_cons);\n+\tin = NTB_DESC_RX_I(qp, idx);\n+\tif (!(in->flags & DESC_DONE_FLAG))\n+\t\treturn 0;\n+\n+\tlink_down = in->flags & LINK_DOWN_FLAG;\n+\tin->flags = 0;\n+\tlen = in->len; /* might be smaller than entry->len */\n+\n+\tentry = (struct ntb_queue_entry *)(uintptr_t)in->data;\n+\tif (WARN_ON(!entry))\n+\t\treturn 0;\n+\n+\tif (link_down) {\n+\t\tntb_qp_link_down(qp);\n+\t\tedma->rx_cons++;\n+\t\tntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q);\n+\t\treturn 1;\n+\t}\n+\n+\tdma_unmap_single(dma_dev, entry->addr, entry->len, DMA_FROM_DEVICE);\n+\n+\tqp->rx_bytes += len;\n+\tqp->rx_pkts++;\n+\tedma->rx_cons++;\n+\n+\tif (qp->rx_handler && qp->client_ready)\n+\t\tqp->rx_handler(qp, qp->cb_data, entry->cb_data, len);\n+\n+\tntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q);\n+\treturn 1;\n+}\n+\n+static void ntb_transport_edma_rx_work(struct work_struct *work)\n+{\n+\tstruct ntb_transport_qp_edma *edma = container_of(\n+\t\t\t\twork, struct ntb_transport_qp_edma, rx_work);\n+\tstruct ntb_transport_qp *qp = edma->qp;\n+\tstruct ntb_transport_ctx_edma *ctx = qp->transport->priv;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < NTB_EDMA_MAX_POLL; i++) {\n+\t\tif (!ntb_transport_edma_rx_complete(qp))\n+\t\t\tbreak;\n+\t}\n+\n+\tif (ntb_transport_edma_rx_complete(qp))\n+\t\tqueue_work(ctx->wq, &edma->rx_work);\n+}\n+\n+static void ntb_transport_edma_tx_work(struct work_struct *work)\n+{\n+\tstruct ntb_transport_qp_edma *edma = container_of(\n+\t\t\t\twork, struct ntb_transport_qp_edma, tx_work);\n+\tstruct ntb_transport_qp *qp = edma->qp;\n+\tstruct ntb_edma_desc *in, __iomem *out;\n+\tstruct ntb_queue_entry *entry;\n+\tunsigned int len;\n+\tvoid *cb_data;\n+\tu32 idx;\n+\n+\twhile (ntb_edma_ring_used_entry(READ_ONCE(edma->tx_issue),\n+\t\t\t\t\tedma->tx_cons) != 0) {\n+\t\t/* Paired with smp_wmb() in ntb_transport_edma_tx_enqueue_inner() */\n+\t\tsmp_rmb();\n+\n+\t\tidx = ntb_edma_ring_idx(edma->tx_cons);\n+\t\tin = NTB_DESC_TX_I(qp, idx);\n+\t\tentry = (struct ntb_queue_entry *)(uintptr_t)in->data;\n+\t\tif (!entry || !(entry->flags & DESC_DONE_FLAG))\n+\t\t\tbreak;\n+\n+\t\tin->data = 0;\n+\n+\t\tcb_data = entry->cb_data;\n+\t\tlen = entry->len;\n+\n+\t\tout = NTB_DESC_TX_O(qp, idx);\n+\n+\t\tWRITE_ONCE(edma->tx_cons, edma->tx_cons + 1);\n+\n+\t\t/*\n+\t\t * No need to add barrier in-between to enforce ordering here.\n+\t\t * The other side proceeds only after both flags and tail are\n+\t\t * updated.\n+\t\t */\n+\t\tiowrite32(entry->flags, &out->flags);\n+\t\tiowrite32(edma->tx_cons, NTB_TAIL_TX_O(qp));\n+\n+\t\tntb_transport_edma_notify_peer(edma);\n+\n+\t\tntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,\n+\t\t\t     &qp->tx_free_q);\n+\n+\t\tif (qp->tx_handler)\n+\t\t\tqp->tx_handler(qp, qp->cb_data, cb_data, len);\n+\n+\t\t/* stat updates */\n+\t\tqp->tx_bytes += len;\n+\t\tqp->tx_pkts++;\n+\t}\n+}\n+\n+static void ntb_transport_edma_tx_cb(void *data,\n+\t\t\t\t     const struct dmaengine_result *res)\n+{\n+\tstruct ntb_queue_entry *entry = data;\n+\tstruct ntb_transport_qp *qp = entry->qp;\n+\tstruct ntb_transport_ctx *nt = qp->transport;\n+\tstruct device *dma_dev = get_dma_dev(qp->ndev);\n+\tenum dmaengine_tx_result dma_err = res->result;\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\n+\tswitch (dma_err) {\n+\tcase DMA_TRANS_READ_FAILED:\n+\tcase DMA_TRANS_WRITE_FAILED:\n+\tcase DMA_TRANS_ABORTED:\n+\t\tentry->errors++;\n+\t\tentry->len = -EIO;\n+\t\tbreak;\n+\tcase DMA_TRANS_NOERROR:\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\tdma_unmap_sg(dma_dev, &entry->sgl, 1, DMA_TO_DEVICE);\n+\tsg_dma_address(&entry->sgl) = 0;\n+\n+\tentry->flags |= DESC_DONE_FLAG;\n+\n+\tqueue_work(ctx->wq, &edma->tx_work);\n+}\n+\n+static int ntb_transport_edma_submit(struct device *d, struct dma_chan *chan,\n+\t\t\t\t     size_t len, void *rc_src, dma_addr_t dst,\n+\t\t\t\t     struct ntb_queue_entry *entry)\n+{\n+\tstruct scatterlist *sgl = &entry->sgl;\n+\tstruct dma_async_tx_descriptor *txd;\n+\tstruct dma_slave_config cfg;\n+\tdma_cookie_t cookie;\n+\tint nents, rc;\n+\n+\tif (!d)\n+\t\treturn -ENODEV;\n+\n+\tif (!chan)\n+\t\treturn -ENXIO;\n+\n+\tif (WARN_ON(!rc_src || !dst))\n+\t\treturn -EINVAL;\n+\n+\tif (WARN_ON(sg_dma_address(sgl)))\n+\t\treturn -EINVAL;\n+\n+\tsg_init_one(sgl, rc_src, len);\n+\tnents = dma_map_sg(d, sgl, 1, DMA_TO_DEVICE);\n+\tif (nents <= 0)\n+\t\treturn -EIO;\n+\n+\tmemset(&cfg, 0, sizeof(cfg));\n+\tcfg.dst_addr       = dst;\n+\tcfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;\n+\tcfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;\n+\tcfg.direction      = DMA_MEM_TO_DEV;\n+\n+\ttxd = dmaengine_prep_slave_sg_config(chan, sgl, 1, DMA_MEM_TO_DEV,\n+\t\t\t\t      DMA_CTRL_ACK | DMA_PREP_INTERRUPT, &cfg);\n+\tif (!txd) {\n+\t\trc = -EIO;\n+\t\tgoto out_unmap;\n+\t}\n+\n+\ttxd->callback_result = ntb_transport_edma_tx_cb;\n+\ttxd->callback_param = entry;\n+\n+\tcookie = dmaengine_submit(txd);\n+\tif (dma_submit_error(cookie)) {\n+\t\trc = -EIO;\n+\t\tgoto out_unmap;\n+\t}\n+\tdma_async_issue_pending(chan);\n+\treturn 0;\n+out_unmap:\n+\tdma_unmap_sg(d, sgl, 1, DMA_TO_DEVICE);\n+\treturn rc;\n+}\n+\n+static int ntb_transport_edma_tx_enqueue_inner(struct ntb_transport_qp *qp,\n+\t\t\t\t\t       struct ntb_queue_entry *entry)\n+{\n+\tstruct device *dma_dev = get_dma_dev(qp->ndev);\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\tstruct ntb_transport_ctx *nt = qp->transport;\n+\tstruct ntb_edma_desc *in, __iomem *out;\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\tunsigned int len = entry->len;\n+\tstruct dma_chan *chan;\n+\tu32 issue, idx, head;\n+\tdma_addr_t dst;\n+\tint rc;\n+\n+\tWARN_ON_ONCE(entry->flags & DESC_DONE_FLAG);\n+\n+\tscoped_guard(spinlock_irqsave, &edma->tx_lock) {\n+\t\thead = READ_ONCE(*NTB_HEAD_TX_I(qp));\n+\t\tissue = edma->tx_issue;\n+\t\tif (ntb_edma_ring_used_entry(head, issue) == 0) {\n+\t\t\tqp->tx_ring_full++;\n+\t\t\treturn -ENOSPC;\n+\t\t}\n+\n+\t\t/*\n+\t\t * ntb_transport_edma_tx_work() checks entry->flags\n+\t\t * so it needs to be set before tx_issue++.\n+\t\t */\n+\t\tidx = ntb_edma_ring_idx(issue);\n+\t\tin = NTB_DESC_TX_I(qp, idx);\n+\t\tin->data = (uintptr_t)entry;\n+\n+\t\t/* Make in->data visible before tx_issue++ */\n+\t\tsmp_wmb();\n+\n+\t\tWRITE_ONCE(edma->tx_issue, edma->tx_issue + 1);\n+\t}\n+\n+\t/* Publish the final transfer length to the other end */\n+\tout = NTB_DESC_TX_O(qp, idx);\n+\tiowrite32(len, &out->len);\n+\tioread32(&out->len);\n+\n+\tif (unlikely(!len)) {\n+\t\tentry->flags |= DESC_DONE_FLAG;\n+\t\tqueue_work(ctx->wq, &edma->tx_work);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Paired with dma_wmb() in ntb_transport_edma_rx_enqueue_inner() */\n+\tdma_rmb();\n+\n+\t/* kick remote eDMA read transfer */\n+\tdst = (dma_addr_t)in->addr;\n+\tchan = ntb_edma_pick_chan(&ctx->chans, qp->qp_num);\n+\trc = ntb_transport_edma_submit(dma_dev, chan, len,\n+\t\t\t\t\t      entry->buf, dst, entry);\n+\tif (rc) {\n+\t\tentry->errors++;\n+\t\tentry->len = -EIO;\n+\t\tentry->flags |= DESC_DONE_FLAG;\n+\t\tqueue_work(ctx->wq, &edma->tx_work);\n+\t}\n+\treturn 0;\n+}\n+\n+static int ntb_transport_edma_tx_enqueue(struct ntb_transport_qp *qp,\n+\t\t\t\t\t struct ntb_queue_entry *entry,\n+\t\t\t\t\t void *cb, void *data, unsigned int len,\n+\t\t\t\t\t unsigned int flags)\n+{\n+\tstruct device *dma_dev;\n+\n+\tif (entry->addr) {\n+\t\t/* Deferred unmap */\n+\t\tdma_dev = get_dma_dev(qp->ndev);\n+\t\tdma_unmap_single(dma_dev, entry->addr, entry->len,\n+\t\t\t\t DMA_TO_DEVICE);\n+\t}\n+\n+\tentry->cb_data = cb;\n+\tentry->buf = data;\n+\tentry->len = len;\n+\tentry->flags = flags;\n+\tentry->errors = 0;\n+\tentry->addr = 0;\n+\n+\tWARN_ON_ONCE(!ntb_qp_edma_enabled(qp));\n+\n+\treturn ntb_transport_edma_tx_enqueue_inner(qp, entry);\n+}\n+\n+static int ntb_transport_edma_rx_enqueue_inner(struct ntb_transport_qp *qp,\n+\t\t\t\t\t       struct ntb_queue_entry *entry)\n+{\n+\tstruct device *dma_dev = get_dma_dev(qp->ndev);\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\tstruct ntb_edma_desc *in, __iomem *out;\n+\tunsigned int len = entry->len;\n+\tvoid *data = entry->buf;\n+\tdma_addr_t dst;\n+\tu32 idx;\n+\tint rc;\n+\n+\tdst = dma_map_single(dma_dev, data, len, DMA_FROM_DEVICE);\n+\trc = dma_mapping_error(dma_dev, dst);\n+\tif (rc)\n+\t\treturn rc;\n+\n+\tguard(spinlock_bh)(&edma->rx_lock);\n+\n+\tif (ntb_edma_ring_full(READ_ONCE(edma->rx_prod),\n+\t\t\t       READ_ONCE(edma->rx_cons))) {\n+\t\trc = -ENOSPC;\n+\t\tgoto out_unmap;\n+\t}\n+\n+\tidx = ntb_edma_ring_idx(edma->rx_prod);\n+\tin = NTB_DESC_RX_I(qp, idx);\n+\tout = NTB_DESC_RX_O(qp, idx);\n+\n+\tiowrite32(len, &out->len);\n+\tiowrite64(dst, &out->addr);\n+\n+\tWARN_ON(in->flags & DESC_DONE_FLAG);\n+\tin->data = (uintptr_t)entry;\n+\tentry->addr = dst;\n+\n+\t/* Ensure len/addr are visible before the head update */\n+\tdma_wmb();\n+\n+\tWRITE_ONCE(edma->rx_prod, edma->rx_prod + 1);\n+\tiowrite32(edma->rx_prod, NTB_HEAD_RX_O(qp));\n+\n+\treturn 0;\n+out_unmap:\n+\tdma_unmap_single(dma_dev, dst, len, DMA_FROM_DEVICE);\n+\treturn rc;\n+}\n+\n+static int ntb_transport_edma_rx_enqueue(struct ntb_transport_qp *qp,\n+\t\t\t\t\t struct ntb_queue_entry *entry)\n+{\n+\tint rc;\n+\n+\trc = ntb_transport_edma_rx_enqueue_inner(qp, entry);\n+\tif (rc) {\n+\t\tntb_list_add(&qp->ntb_rx_q_lock, &entry->entry,\n+\t\t\t     &qp->rx_free_q);\n+\t\treturn rc;\n+\t}\n+\n+\tntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_pend_q);\n+\n+\tif (qp->active)\n+\t\ttasklet_schedule(&qp->rxc_db_work);\n+\n+\treturn 0;\n+}\n+\n+static void ntb_transport_edma_rx_poll(struct ntb_transport_qp *qp)\n+{\n+\tstruct ntb_transport_ctx *nt = qp->transport;\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\n+\tqueue_work(ctx->wq, &edma->rx_work);\n+\tqueue_work(ctx->wq, &edma->tx_work);\n+}\n+\n+static int ntb_transport_edma_qp_init(struct ntb_transport_ctx *nt,\n+\t\t\t\t      unsigned int qp_num)\n+{\n+\tstruct ntb_transport_qp *qp = &nt->qp_vec[qp_num];\n+\tstruct ntb_transport_qp_edma *edma;\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tint node;\n+\n+\tnode = dev_to_node(&ndev->dev);\n+\n+\tqp->priv = kzalloc_node(sizeof(*edma), GFP_KERNEL, node);\n+\tif (!qp->priv)\n+\t\treturn -ENOMEM;\n+\n+\tedma = (struct ntb_transport_qp_edma *)qp->priv;\n+\tedma->qp = qp;\n+\tedma->rx_prod = 0;\n+\tedma->rx_cons = 0;\n+\tedma->tx_cons = 0;\n+\tedma->tx_issue = 0;\n+\n+\tspin_lock_init(&edma->rx_lock);\n+\tspin_lock_init(&edma->tx_lock);\n+\n+\tINIT_WORK(&edma->db_work, ntb_transport_edma_db_work);\n+\tINIT_WORK(&edma->rx_work, ntb_transport_edma_rx_work);\n+\tINIT_WORK(&edma->tx_work, ntb_transport_edma_tx_work);\n+\n+\treturn 0;\n+}\n+\n+static void ntb_transport_edma_qp_free(struct ntb_transport_qp *qp)\n+{\n+\tstruct ntb_transport_qp_edma *edma = qp->priv;\n+\n+\tcancel_work_sync(&edma->db_work);\n+\tcancel_work_sync(&edma->rx_work);\n+\tcancel_work_sync(&edma->tx_work);\n+\n+\tkfree(qp->priv);\n+}\n+\n+static int ntb_transport_edma_pre_link_up(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct pci_dev *pdev = ndev->pdev;\n+\tint rc;\n+\n+\trc = ntb_transport_edma_ep_init(nt);\n+\tif (rc)\n+\t\tdev_err(&pdev->dev, \"Failed to init EP: %d\\n\", rc);\n+\n+\treturn rc;\n+}\n+\n+static int ntb_transport_edma_post_link_up(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct pci_dev *pdev = ndev->pdev;\n+\tint rc;\n+\n+\trc = ntb_transport_edma_rc_init(nt);\n+\tif (rc)\n+\t\tdev_err(&pdev->dev, \"Failed to init RC: %d\\n\", rc);\n+\n+\treturn rc;\n+}\n+\n+static int ntb_transport_edma_enable(struct ntb_transport_ctx *nt,\n+\t\t\t\t     unsigned int *mw_count)\n+{\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tstruct ntb_transport_ctx_edma *ctx = nt->priv;\n+\n+\tif (!use_remote_edma)\n+\t\treturn 0;\n+\n+\t/*\n+\t * We need at least one MW for the transport plus one MW reserved\n+\t * for the remote eDMA window (see ntb_edma_setup_mws/peer).\n+\t */\n+\tif (*mw_count <= 1) {\n+\t\tdev_err(&ndev->dev,\n+\t\t\t\"remote eDMA requires at least two MWS (have %u)\\n\",\n+\t\t\t*mw_count);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tctx->wq = alloc_workqueue(\"ntb-edma-wq\", WQ_UNBOUND | WQ_SYSFS, 0);\n+\tif (!ctx->wq) {\n+\t\tntb_transport_edma_uninit(nt);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\t/* Reserve the last peer MW exclusively for the eDMA window. */\n+\t*mw_count -= 1;\n+\n+\treturn 0;\n+}\n+\n+static void ntb_transport_edma_disable(struct ntb_transport_ctx *nt)\n+{\n+\tntb_transport_edma_uninit(nt);\n+}\n+\n+static const struct ntb_transport_backend_ops edma_backend_ops = {\n+\t.enable = ntb_transport_edma_enable,\n+\t.disable = ntb_transport_edma_disable,\n+\t.qp_init = ntb_transport_edma_qp_init,\n+\t.qp_free = ntb_transport_edma_qp_free,\n+\t.pre_link_up = ntb_transport_edma_pre_link_up,\n+\t.post_link_up = ntb_transport_edma_post_link_up,\n+\t.setup_qp_mw = ntb_transport_edma_setup_qp_mw,\n+\t.tx_free_entry = ntb_transport_edma_tx_free_entry,\n+\t.tx_enqueue = ntb_transport_edma_tx_enqueue,\n+\t.rx_enqueue = ntb_transport_edma_rx_enqueue,\n+\t.rx_poll = ntb_transport_edma_rx_poll,\n+\t.debugfs_stats_show = ntb_transport_edma_debugfs_stats_show,\n+};\n+\n+int ntb_transport_edma_init(struct ntb_transport_ctx *nt)\n+{\n+\tstruct ntb_dev *ndev = nt->ndev;\n+\tint node;\n+\n+\tnode = dev_to_node(&ndev->dev);\n+\tnt->priv = kzalloc_node(sizeof(struct ntb_transport_ctx_edma), GFP_KERNEL,\n+\t\t\t\tnode);\n+\tif (!nt->priv)\n+\t\treturn -ENOMEM;\n+\n+\tnt->backend_ops = edma_backend_ops;\n+\t/*\n+\t * On remote eDMA mode, one DMA read channel is used for Host side\n+\t * to interrupt EP.\n+\t */\n+\tuse_msi = false;\n+\treturn 0;\n+}\ndiff --git a/drivers/ntb/ntb_transport_internal.h b/drivers/ntb/ntb_transport_internal.h\nindex 51ff08062d73..9fff65980d3d 100644\n--- a/drivers/ntb/ntb_transport_internal.h\n+++ b/drivers/ntb/ntb_transport_internal.h\n@@ -8,6 +8,7 @@\n extern unsigned long max_mw_size;\n extern unsigned int transport_mtu;\n extern bool use_msi;\n+extern bool use_remote_edma;\n \n #define QP_TO_MW(nt, qp)\t((qp) % nt->mw_count)\n \n@@ -29,6 +30,11 @@ struct ntb_queue_entry {\n \t\tstruct ntb_payload_header __iomem *tx_hdr;\n \t\tstruct ntb_payload_header *rx_hdr;\n \t};\n+\n+#ifdef CONFIG_NTB_TRANSPORT_EDMA\n+\tdma_addr_t addr;\n+\tstruct scatterlist sgl;\n+#endif\n };\n \n struct ntb_rx_info {\n@@ -202,4 +208,13 @@ int ntb_transport_init_queue(struct ntb_transport_ctx *nt,\n \t\t\t     unsigned int qp_num);\n struct device *get_dma_dev(struct ntb_dev *ndev);\n \n+#ifdef CONFIG_NTB_TRANSPORT_EDMA\n+int ntb_transport_edma_init(struct ntb_transport_ctx *nt);\n+#else\n+static inline int ntb_transport_edma_init(struct ntb_transport_ctx *nt)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+#endif /* CONFIG_NTB_TRANSPORT_EDMA */\n+\n #endif /* _NTB_TRANSPORT_INTERNAL_H_ */\n",
    "prefixes": [
        "RFC",
        "v3",
        "26/35"
    ]
}