Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2180678/?format=api
{ "id": 2180678, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2180678/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linuxppc-dev/patch/20260107091823.68974-12-jniethe@nvidia.com/", "project": { "id": 2, "url": "http://patchwork.ozlabs.org/api/1.1/projects/2/?format=api", "name": "Linux PPC development", "link_name": "linuxppc-dev", "list_id": "linuxppc-dev.lists.ozlabs.org", "list_email": "linuxppc-dev@lists.ozlabs.org", "web_url": "https://github.com/linuxppc/wiki/wiki", "scm_url": "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git", "webscm_url": "https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git/" }, "msgid": "<20260107091823.68974-12-jniethe@nvidia.com>", "date": "2026-01-07T09:18:23", "name": "[v2,11/11] mm: Remove device private pages from the physical address space", "commit_ref": null, "pull_url": null, "state": "handled-elsewhere", "archived": false, "hash": "a718344f404c14b6af400f075bfd35ea16b2c604", "submitter": { "id": 92354, "url": "http://patchwork.ozlabs.org/api/1.1/people/92354/?format=api", "name": "Jordan Niethe", "email": "jniethe@nvidia.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linuxppc-dev/patch/20260107091823.68974-12-jniethe@nvidia.com/mbox/", "series": [ { "id": 487451, "url": "http://patchwork.ozlabs.org/api/1.1/series/487451/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=487451", "date": "2026-01-07T09:18:12", "name": "Remove device private pages from physical address space", "version": 2, "mbox": "http://patchwork.ozlabs.org/series/487451/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2180678/comments/", "check": "fail", "checks": "http://patchwork.ozlabs.org/api/patches/2180678/checks/", "tags": {}, "headers": { "Return-Path": "\n <linuxppc-dev+bounces-15374-incoming=patchwork.ozlabs.org@lists.ozlabs.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linuxppc-dev@lists.ozlabs.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=ud2GIAiR;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:21b9:f100::1; helo=lists.ozlabs.org;\n envelope-from=linuxppc-dev+bounces-15374-incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)", "lists.ozlabs.org;\n arc=pass smtp.remote-ip=52.101.56.71 arc.chain=microsoft.com", "lists.ozlabs.org;\n dmarc=pass (p=reject dis=none) header.from=nvidia.com", "lists.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=ud2GIAiR;\n\tdkim-atps=neutral", "lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nvidia.com\n (client-ip=52.101.56.71; helo=bn1pr04cu002.outbound.protection.outlook.com;\n envelope-from=jniethe@nvidia.com; receiver=lists.ozlabs.org)", "dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=nvidia.com;" ], "Received": [ "from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:21b9:f100::1])\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 4dmMvk3s8Pz1xqd\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 07 Jan 2026 20:20:34 +1100 (AEDT)", "from boromir.ozlabs.org (localhost [127.0.0.1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4dmMtP3DpPz2yY9;\n\tWed, 07 Jan 2026 20:19:25 +1100 (AEDT)", "from BN1PR04CU002.outbound.protection.outlook.com\n (mail-eastus2azon11010071.outbound.protection.outlook.com [52.101.56.71])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange secp256r1 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4dmMtN0Dj4z2yv9\n\tfor <linuxppc-dev@lists.ozlabs.org>; Wed, 07 Jan 2026 20:19:24 +1100 (AEDT)", "from DM4PR12MB9072.namprd12.prod.outlook.com (2603:10b6:8:be::6) by\n MN2PR12MB4335.namprd12.prod.outlook.com (2603:10b6:208:1d4::13) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9499.2; Wed, 7 Jan\n 2026 09:19:15 +0000", "from DM4PR12MB9072.namprd12.prod.outlook.com\n ([fe80::9e49:782:8e98:1ff1]) by DM4PR12MB9072.namprd12.prod.outlook.com\n ([fe80::9e49:782:8e98:1ff1%5]) with mapi id 15.20.9499.002; Wed, 7 Jan 2026\n 09:19:15 +0000" ], "ARC-Seal": [ "i=2; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1767777565;\n\tcv=pass;\n b=TQ6pt+loRKNugycLxiL7XcvubQm69fy5jMUQzkwWRfK0LLeUAn0ir4uOYJTjL9+/Glj0dGZO+IfwW366TRGbyg9c9Bq0SS5gjABtKIAwTzUi2oUPr8NYKRNckHDr4iLBs5Jdx7U6Zq3fjEIYo/dX5523o8h1JSYm2ZbfK6BOl/7LjGRf9lx8K6czDyY5xMdJK9Qe404oib7Vi2yVrewpcAYHGAzTBEgq1cvu/6uNCDFn9wGxsUl5QDN+uwmBf+hcm/m4oF9MTPy+2D95DjkeM2ukKisoVJCkPlbytjfq/5Ks2i241orb8gVBtwLF8EU2uRX9cnQqNgmjXnlJAAjaFg==", "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=e9L2nZhdrcFtvoUUdAdrcpYy+v1kiSx5G6K90tvSSN+2WFaUMdsyEaZWybCcK06uuGAC3XNZdDInLsM/OvXtTYfYB/fDpi40bJljYyEg8repOjFhvjUPuaZIyt16qPH3QszxLHqLRFZ5IHWxhdaWBzPfRdqJlgt8ezA57ZO1cBXOrV7F3Z44ehhpOJitIbAOcojMBzLRJvsArJDWbiYrQNKulhqGA96htrVJRsEMBcAieJ2mJdb+TZhZHQWq17jcOItCDyMliw5pIg4WU9S0n+zKBMcbzEpfrl6oBWgjUvABBSMoTA1LE0WnZ/C+Ps8fI0Zq4O9k/6+5aS5S6NyN2g==" ], "ARC-Message-Signature": [ "i=2; a=rsa-sha256; d=lists.ozlabs.org; s=201707;\n\tt=1767777565; c=relaxed/relaxed;\n\tbh=HtcWKucFsZkDVDAu3g0xVqexFcnMtLPneHWdLl7WLag=;\n\th=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:\n\t Content-Type:MIME-Version;\n b=iQKRC5xcfIWXolPRHLoBGcZBmOzDu1tSxzUMBbvzCktVeYZoRosld7lX2umQP/vTSRoQHf5gMmeuuayBfDf2Eq91SaVvh+n4c7D4lc6TYvRWuXmoh0CJlDDK0ZXo/lvZVNkSWnA/MYyLYd0Zt96WTNN48X1Vl/5f6TseIAus82pcVz7B0tpLrBuGCb3MRGh1cS6JXhKKtBJSBsYd2+vj675aKdGZV1poEaKcCW66AAlQIBcXCKbPc8+i3xiZDu5m8ffqS/cdC/gwFMc5JTbFn3woQbcNAHEZQTXw/GwlsCUBQchLpfBucJXcIo5qpGjecH0fegDxg2UNsqDSDiu89g==", "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=HtcWKucFsZkDVDAu3g0xVqexFcnMtLPneHWdLl7WLag=;\n b=gfASZZ1m8CQmNNzFSlXVd/U9M/4qrgaKncGtZwHL20hccYFHe98Nnm9CDYU4hSNBBqsKS/L5WE9gSIQ2+r/pZL708JPJiAa/Hm5z5Z4pn06d8WLJvNM6oig33qba8/8KkfORCAT9XIhqNWMNrW+cef8ppXvNOCM5KC77I/h2RwwrUBCgVBO38k/3laT1tRX05AuwRY+JtWqBOHR/P8Y9MY8JrLrVsmwoWuT4n0QcixBPNKC31mQkUQTEktEEx/caCSvlFujeYxcmFmzJWX8kx4O5Wq9jODenngIn84/Q4Kh+HUQ5t62UPR3UtNAUJwF9qXBKopqStbhkWsQveY8JZw==" ], "ARC-Authentication-Results": [ "i=2; lists.ozlabs.org;\n dmarc=pass (p=reject dis=none) header.from=nvidia.com;\n dkim=pass (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=ud2GIAiR; dkim-atps=neutral;\n spf=pass (client-ip=52.101.56.71;\n helo=bn1pr04cu002.outbound.protection.outlook.com;\n envelope-from=jniethe@nvidia.com;\n receiver=lists.ozlabs.org) smtp.mailfrom=nvidia.com", "i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com;\n dkim=pass header.d=nvidia.com; arc=none" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=HtcWKucFsZkDVDAu3g0xVqexFcnMtLPneHWdLl7WLag=;\n b=ud2GIAiRtvVeLUhFxO3FGNyeP9BJcrcvCCMc1zln5vSdmgC5hVmumz2GUj1zjC+Ou0FvonMatxrqIYHwEyDmtxWFnIx64l5JwwdXCB4GfuMnybqOfHLTQigQlShj/43sDlyDs7/uMJ5FfirXsxWNWmiKL2uXWdCYEG9PYy2FPdvwFH2ML+FO0puOZnt2eAi8psoauJlle7IsVSOxem41RSYKmJgocnPg31iIkc6oDT1mKidactcw4uJ8Du3dxdeLOMnW6d+CxAtBPCUavD3BNbUa1rWVyRjCdXsbuIG7aROiZgyB+ZuoQk0m67vJ4vqmwE369B0K/4iI0jTdSu11ug==", "From": "Jordan Niethe <jniethe@nvidia.com>", "To": "linux-mm@kvack.org", "Cc": "balbirs@nvidia.com,\n\tmatthew.brost@intel.com,\n\takpm@linux-foundation.org,\n\tlinux-kernel@vger.kernel.org,\n\tdri-devel@lists.freedesktop.org,\n\tdavid@redhat.com,\n\tziy@nvidia.com,\n\tapopple@nvidia.com,\n\tlorenzo.stoakes@oracle.com,\n\tlyude@redhat.com,\n\tdakr@kernel.org,\n\tairlied@gmail.com,\n\tsimona@ffwll.ch,\n\trcampbell@nvidia.com,\n\tmpenttil@redhat.com,\n\tjgg@nvidia.com,\n\twilly@infradead.org,\n\tlinuxppc-dev@lists.ozlabs.org,\n\tintel-xe@lists.freedesktop.org,\n\tjgg@ziepe.ca,\n\tFelix.Kuehling@amd.com", "Subject": "[PATCH v2 11/11] mm: Remove device private pages from the physical\n address space", "Date": "Wed, 7 Jan 2026 20:18:23 +1100", "Message-Id": "<20260107091823.68974-12-jniethe@nvidia.com>", "X-Mailer": "git-send-email 2.34.1", "In-Reply-To": "<20260107091823.68974-1-jniethe@nvidia.com>", "References": "<20260107091823.68974-1-jniethe@nvidia.com>", "Content-Transfer-Encoding": "8bit", "Content-Type": "text/plain", "X-ClientProxiedBy": "BYAPR08CA0053.namprd08.prod.outlook.com\n (2603:10b6:a03:117::30) To DM4PR12MB9072.namprd12.prod.outlook.com\n (2603:10b6:8:be::6)", "X-Mailing-List": "linuxppc-dev@lists.ozlabs.org", "List-Id": "<linuxppc-dev.lists.ozlabs.org>", "List-Help": "<mailto:linuxppc-dev+help@lists.ozlabs.org>", "List-Owner": "<mailto:linuxppc-dev+owner@lists.ozlabs.org>", "List-Post": "<mailto:linuxppc-dev@lists.ozlabs.org>", "List-Archive": "<https://lore.kernel.org/linuxppc-dev/>,\n <https://lists.ozlabs.org/pipermail/linuxppc-dev/>", "List-Subscribe": "<mailto:linuxppc-dev+subscribe@lists.ozlabs.org>,\n <mailto:linuxppc-dev+subscribe-digest@lists.ozlabs.org>,\n <mailto:linuxppc-dev+subscribe-nomail@lists.ozlabs.org>", "List-Unsubscribe": "<mailto:linuxppc-dev+unsubscribe@lists.ozlabs.org>", "Precedence": "list", "MIME-Version": "1.0", "X-MS-PublicTrafficType": "Email", "X-MS-TrafficTypeDiagnostic": "DM4PR12MB9072:EE_|MN2PR12MB4335:EE_", "X-MS-Office365-Filtering-Correlation-Id": "31a77b30-d652-4d56-52f8-08de4dcdd37c", "X-MS-Exchange-SenderADCheck": "1", "X-MS-Exchange-AntiSpam-Relay": "0", "X-Microsoft-Antispam": "BCL:0;ARA:13230040|7416014|376014|1800799024|366016;", "X-Microsoft-Antispam-Message-Info": "\n 4hV3EJ6WBeYbAPh+2CgPHf7H0vLLGGGf4I18V7M7J7eqz6cIUskM8CwF02t7DAqOEn85IGVDrdYw288y8JGosB5QZkIGLiVWkRFl8dPQG2C61/eR4+qXgGZiJvq/D8jVAKTiHLpzGCBlluB5BztNJlWE3xYZUH0r54mtR0lsQfkyEHFLuMML3/EULiHe7yWPFV9z8OMzTVIi07kaE5GnQt+PR8WngMlLkTi8VmSND98xb+p13gjnOkCQ+KQ4tPEPBFzVoAhZOcBfzeA51soBo1Yj7EfLZYdZSr0E9lqcwjTTSUu4QZeuhAcvydEcWCtQVL/2etko7OQ+ShJfN9ilgHnXt2boiUZN/jcjew21j/xDXDnJ5GECM33DnUbukilp9AAHy5iQlX03EwqOtTnV33h0zd1Voue8twXuGIC+g9riREymODjz6kujx4iEA8AtQA25BWXwgLRatYLrqVgG8dNTDvQNNDvzeG9n7G+GmEX5TMA+CBW44yO2pGrtaEgLfKVGedoWE/rpOJemYZIvUSUaiy6JgebVRIUcMEmBtY0kSCTUI4u6yfLP7WBk6B1AfQpIR7RRYv/UQUvhysH4WV6EXQhk5/CDEOvWysaGjHxCkxcUMnakX38mM85KMwLQVsC7FIjG613HQa1kDep+6kfdbXbeL+Rtf5V/xCzf4H7LEb9m3WhuntuYKStvSoxnkrbMq20JvfzgfgxFriGisti6EWM6ZDWEAttMxkFM2TqO/WcoezQpqm+UX0Zi7xZ7QwM2oJFF7JAmeseYWwtNs5rxuo9Xwd7mTNcC6wEZWossnkb6pSGby1j+GaEwM1aiIvQkqvO6mG+7m8kY4reXtIhJpPL279smmbqEZ4IaRzXTM+ee5oASxQvMCL0RB3ITys+f1VxHzs4w3bBs4znoFx03L/MDaUQmm3wa65Rrmnjy+YROlq0cmeysSzvvQzl6noudC5nusXcYaHEfvU8YvfgQocoCYMdcylyzndJk+6TFm4o+cQfBx4uFZtuWoVzeFTcTklYs8TufifhFb2zvzQOM39/3UzkTO8JxUE8Ij+WJ6dww2xkPQ2NemrlrJPA5vJ9cD9bD/NCqLF4aw88F+XM7KqT1aT4Zn+81oTaHMjhT2DyXKkAPFCosJbaDyiroeU3jtKAagpUd/1cpAdifmGaRB3LNAtelb9njBNZ8xL3LVch/Uxe5SSS15+lEMV2lGhXm3cb/p1++mfenmxGZPwGm8/oVvuF70vWjN/1RD1saZ8YQF4Kz9cW27wXWgRhXexQZfTga1qhOhNAytecfYlUegR9XZ/OkeQ4JW9/aID/RFFaARv5kA1txzxf32DuF1nkTxc4AnfBX6xy98Sjo26h+Dd+PbjTC8OAeXiyhB3zItjw7lAbqGgWAEFBqS7k/WWLgwQMPT0eTmMeKYTFKn0quzr8zxG/WelXwx+TdFfPi/Gu0QZ7YYQ1H+X752NGz", "X-Forefront-Antispam-Report": "\n\tCIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:DM4PR12MB9072.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(7416014)(376014)(1800799024)(366016);DIR:OUT;SFP:1101;", "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1", "X-MS-Exchange-AntiSpam-MessageData-0": "\n /zpS/hbxQXGhGn5CHG8N1bweZmkFT+vPj0ELog3Z8JEPiYFhpYxdsHdFhZcuGcVCK7Z392/xUCByGeWJSbWVNvAKetXU755X3PIFfuKcliVwnoyfAAeG3K5kqgNFdYLSqBDXX33wyePjg5nBiMkDphujfLQaqLL+OaTuEScx98H8sJgzl5FaBXGQp3sobwN2zS3lX9AydN17id32Qh4uc9ZUVRSOZwg6gtuTjWnREfM5Mxnvef78jw578+gzyUGkKs/M+DqzV6UfyW2RlgR6IEcpQ0DA2O86Jv9kLTTKEULRZnM4dSilKtG0TdH09im2SNmZXgOP/bXcKBOveibzmNIy9pk4sqnAoa9XFZWZ4u2U5cKjDwqaQvADlb1h9SpCkF0bfBjEhVmVNyava8jNKdI1sVo+GjEmpk2l08+N6ApMqU0D95E5+NNQ2tUky43PgkkXY6JHS3AOgZF3+44X6pOTbRO6ZitX8/M32JfFOKtUANOBcG3uw5rOpnao+CywGHZ1I0OgCh325uWoFew0gGDp55ctabss9QccNbIm7zcVU4xBIRYsJRx8OfMD8t9EZXQCl+0T69Nrn8uvEI1fbDYCwlw4YEtoTYSJcNn5MW2PaVteKlkrkoBWOsVnd5qCx6/4SzUkZcnAKX4jY9isgxYxFa8fk06BvQci1JeJrI/ZjTv513+FCGNzwKj4RRerIAD2v5IAYe/+zskcnpDidNuyxeyM8GYRMIu/3/d6xbRFNJ61dfC/c68zLoszrltU4XfiWqh6LKjoKSYkfRoY802gWbRxeAR0UtCWiRl8NCgy1qG4q3aDf6Py8rjguU/HNlrB0128y9yKmM8VNETs9ZZgxzVXfHiRn1e/NKzzN1nZMxfnIJn8Gxpb3gF7DR2aLGv936QxblC1Rm9V1uGMuGy+2keP/nKVnvfYWvx6O0g3N5xOajGktQOEoG75CY0R9tixO68lW9e+hcE8gRAYZk3+GDEpefCBcPz2mFbiYpylo4Q2qkA2bsHpEC2sqirqcwtCIkw6Jqrm+v8fG43uPrl69Y+AC6TVRgvZiD9UKjlgacw4YEWvbqPkijgvqKnn53lD+3RIXLCvF2w/NucA3ask2jahDmJr4APLQGZJmyV4vl5kNmjRH4bGCtaJVRZBaWIosxFpq1BdOkMFUqph9Zht67tvLVHhqM9rP//TNLTr3KPXQPTxySlEfzbJtQo2Qn2jNy2rr94jc3KsF+jGbJLZOAqQY6jDKNutgVKKNOVx/1ffhyAVfcUCoZCyeNpqCJb6PLaTYoTgEFEjftbSBZf9wyDVEA7W+t5h2QBcPcELGIlp/yo1oOIGdaexN9i5jxalpi7hmTJSajEPWLsaFNnY/xJPtavQLTzzNJywy9u72LJMg4HG3gJZeCvUGp0+HnT4wXLpynoylEeLiP6MEm3fPEZxFVF0nvpuB6GKIpQKu7TkmC6g2ClzAg8As8DOMTy6VNoAhsClSBvcfqDCARF7FuJZkE9/TEoV7EmiWpgvV/VJpECGP/sFk48o880ovTW+TeKqKYgBWSjDj6fVDDkt3KowpXFmn/CD5OI54HkXfoMvkFmycV1jW+LZ2sJe1bhs8C+sQsUGJRS0YowAL6cASBp3/73ywUTZOyjSt5UJa55qr/BTaqw+2dAqt0fqx3Ni3rIz+XsBSLkNliWsUlBgJL6h/B4cMIlhYekIa/pAQ8Ma6ypCGfKg9Fn0dXY3+NzeplB9wAlKjIxCj7IwvQ==", "X-OriginatorOrg": "Nvidia.com", "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 31a77b30-d652-4d56-52f8-08de4dcdd37c", "X-MS-Exchange-CrossTenant-AuthSource": "DM4PR12MB9072.namprd12.prod.outlook.com", "X-MS-Exchange-CrossTenant-AuthAs": "Internal", "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "07 Jan 2026 09:19:14.8660\n (UTC)", "X-MS-Exchange-CrossTenant-FromEntityHeader": "Hosted", "X-MS-Exchange-CrossTenant-Id": "43083d15-7273-40c1-b7db-39efd9ccc17a", "X-MS-Exchange-CrossTenant-MailboxType": "HOSTED", "X-MS-Exchange-CrossTenant-UserPrincipalName": "\n AVNjhbtwEjfzOmw+y5CGx6oWbYbspNWZ1mWliW9w+Iqiy2ingrytWbJpk4+ZBapvgDxvDky+ND75nhYdmgQBVQ==", "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "MN2PR12MB4335", "X-Spam-Status": "No, score=-0.2 required=3.0 tests=ARC_SIGNED,ARC_VALID,\n\tDKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,\n\tRCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS\n\tautolearn=disabled version=4.0.1 OzLabs 8", "X-Spam-Checker-Version": "SpamAssassin 4.0.1 (2024-03-25) on lists.ozlabs.org" }, "content": "Currently when creating these device private struct pages, the first\nstep is to use request_free_mem_region() to get a range of physical\naddress space large enough to represent the devices memory. This\nallocated physical address range is then remapped as device private\nmemory using memremap_pages().\n\nNeeding allocation of physical address space has some problems:\n\n 1) There may be insufficient physical address space to represent the\n device memory. KASLR reducing the physical address space and VM\n configurations with limited physical address space increase the\n likelihood of hitting this especially as device memory increases. This\n has been observed to prevent device private from being initialized.\n\n 2) Attempting to add the device private pages to the linear map at\n addresses beyond the actual physical memory causes issues on\n architectures like aarch64 meaning the feature does not work there.\n\nInstead of using the physical address space, introduce a device private\naddress space and allocate devices regions from there to represent the\ndevice private pages.\n\nIntroduce a new interface memremap_device_private_pagemap() that\nallocates a requested amount of device private address space and creates\nthe necessary device private pages.\n\nTo support this new interface, struct dev_pagemap needs some changes:\n\n - Add a new dev_pagemap::nr_pages field as an input parameter.\n - Add a new dev_pagemap::pages array to store the device\n private pages.\n\nWhen using memremap_device_private_pagemap(), rather then passing in\ndev_pagemap::ranges[dev_pagemap::nr_ranges] of physical address space to\nbe remapped, dev_pagemap::nr_ranges will always be 1, and the device\nprivate range that is reserved is returned in dev_pagemap::range.\n\nForbid calling memremap_pages() with dev_pagemap::ranges::type =\nMEMORY_DEVICE_PRIVATE.\n\nRepresent this device private address space using a new\ndevice_private_pgmap_tree maple tree. This tree maps a given device\nprivate address to a struct dev_pagemap, where a specific device private\npage may then be looked up in that dev_pagemap::pages array.\n\nDevice private address space can be reclaimed and the assoicated device\nprivate pages freed using the corresponding new\nmemunmap_device_private_pagemap() interface.\n\nBecause the device private pages now live outside the physical address\nspace, they no longer have a normal PFN. This means that page_to_pfn(),\net al. are no longer meaningful.\n\nIntroduce helpers:\n\n - device_private_page_to_offset()\n - device_private_folio_to_offset()\n\nto take a given device private page / folio and return its offset within\nthe device private address space.\n\nUpdate the places where we previously converted a device private page to\na PFN to use these new helpers. When we encounter a device private\noffset, instead of looking up its page within the pagemap use\ndevice_private_offset_to_page() instead.\n\nUpdate the existing users:\n\n - lib/test_hmm.c\n - ppc ultravisor\n - drm/amd/amdkfd\n - gpu/drm/xe\n - gpu/drm/nouveau\n\nto use the new memremap_device_private_pagemap() interface.\n\nSigned-off-by: Jordan Niethe <jniethe@nvidia.com>\nSigned-off-by: Alistair Popple <apopple@nvidia.com>\n\n---\n\nNOTE: The updates to the existing drivers have only been compile tested.\nI'll need some help in testing these drivers.\n\nv1:\n- Include NUMA node paramater for memremap_device_private_pagemap()\n- Add devm_memremap_device_private_pagemap() and friends\n- Update existing users of memremap_pages():\n - ppc ultravisor\n - drm/amd/amdkfd\n - gpu/drm/xe\n - gpu/drm/nouveau\n- Update for HMM huge page support\n- Guard device_private_offset_to_page and friends with CONFIG_ZONE_DEVICE\n\nv2:\n- Make sure last member of struct dev_pagemap remains DECLARE_FLEX_ARRAY(struct range, ranges);\n---\n Documentation/mm/hmm.rst | 11 +-\n arch/powerpc/kvm/book3s_hv_uvmem.c | 41 ++---\n drivers/gpu/drm/amd/amdkfd/kfd_migrate.c | 23 +--\n drivers/gpu/drm/nouveau/nouveau_dmem.c | 35 ++--\n drivers/gpu/drm/xe/xe_svm.c | 28 +---\n include/linux/hmm.h | 3 +\n include/linux/leafops.h | 16 +-\n include/linux/memremap.h | 64 +++++++-\n include/linux/migrate.h | 6 +-\n include/linux/mm.h | 2 +\n include/linux/rmap.h | 5 +-\n include/linux/swapops.h | 10 +-\n lib/test_hmm.c | 69 ++++----\n mm/debug.c | 9 +-\n mm/memremap.c | 193 ++++++++++++++++++-----\n mm/mm_init.c | 8 +-\n mm/page_vma_mapped.c | 19 ++-\n mm/rmap.c | 43 +++--\n mm/util.c | 5 +-\n 19 files changed, 391 insertions(+), 199 deletions(-)", "diff": "diff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst\nindex 7d61b7a8b65b..27067a6a2408 100644\n--- a/Documentation/mm/hmm.rst\n+++ b/Documentation/mm/hmm.rst\n@@ -276,17 +276,12 @@ These can be allocated and freed with::\n struct resource *res;\n struct dev_pagemap pagemap;\n \n- res = request_free_mem_region(&iomem_resource, /* number of bytes */,\n- \"name of driver resource\");\n pagemap.type = MEMORY_DEVICE_PRIVATE;\n- pagemap.range.start = res->start;\n- pagemap.range.end = res->end;\n- pagemap.nr_range = 1;\n+ pagemap.nr_pages = /* number of pages */;\n pagemap.ops = &device_devmem_ops;\n- memremap_pages(&pagemap, numa_node_id());\n+ memremap_device_private_pagemap(&pagemap, numa_node_id());\n \n- memunmap_pages(&pagemap);\n- release_mem_region(pagemap.range.start, range_len(&pagemap.range));\n+ memunmap_device_private_pagemap(&pagemap);\n \n There are also devm_request_free_mem_region(), devm_memremap_pages(),\n devm_memunmap_pages(), and devm_release_mem_region() when the resources can\ndiff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c\nindex 67910900af7b..948747db8231 100644\n--- a/arch/powerpc/kvm/book3s_hv_uvmem.c\n+++ b/arch/powerpc/kvm/book3s_hv_uvmem.c\n@@ -636,7 +636,7 @@ void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot *slot,\n \t\tmutex_lock(&kvm->arch.uvmem_lock);\n \n \t\tif (kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) {\n-\t\t\tuvmem_page = pfn_to_page(uvmem_pfn);\n+\t\t\tuvmem_page = device_private_offset_to_page(uvmem_pfn);\n \t\t\tpvt = uvmem_page->zone_device_data;\n \t\t\tpvt->skip_page_out = skip_page_out;\n \t\t\tpvt->remove_gfn = true;\n@@ -721,7 +721,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long gpa, struct kvm *kvm)\n \tpvt->gpa = gpa;\n \tpvt->kvm = kvm;\n \n-\tdpage = pfn_to_page(uvmem_pfn);\n+\tdpage = device_private_offset_to_page(uvmem_pfn);\n \tdpage->zone_device_data = pvt;\n \tzone_device_page_init(dpage, 0);\n \treturn dpage;\n@@ -888,7 +888,7 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa,\n \tsrcu_idx = srcu_read_lock(&kvm->srcu);\n \tmutex_lock(&kvm->arch.uvmem_lock);\n \tif (kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) {\n-\t\tuvmem_page = pfn_to_page(uvmem_pfn);\n+\t\tuvmem_page = device_private_offset_to_page(uvmem_pfn);\n \t\tpvt = uvmem_page->zone_device_data;\n \t\tpvt->skip_page_out = true;\n \t\t/*\n@@ -906,7 +906,7 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, unsigned long gpa,\n \n \tmutex_lock(&kvm->arch.uvmem_lock);\n \tif (kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) {\n-\t\tuvmem_page = pfn_to_page(uvmem_pfn);\n+\t\tuvmem_page = device_private_offset_to_page(uvmem_pfn);\n \t\tpvt = uvmem_page->zone_device_data;\n \t\tpvt->skip_page_out = true;\n \t\tpvt->remove_gfn = false; /* it continues to be a valid GFN */\n@@ -1017,7 +1017,7 @@ static vm_fault_t kvmppc_uvmem_migrate_to_ram(struct vm_fault *vmf)\n static void kvmppc_uvmem_folio_free(struct folio *folio)\n {\n \tstruct page *page = &folio->page;\n-\tunsigned long pfn = page_to_pfn(page) -\n+\tunsigned long pfn = device_private_page_to_offset(page) -\n \t\t\t(kvmppc_uvmem_pgmap.range.start >> PAGE_SHIFT);\n \tstruct kvmppc_uvmem_page_pvt *pvt;\n \n@@ -1159,8 +1159,6 @@ int kvmppc_uvmem_init(void)\n {\n \tint ret = 0;\n \tunsigned long size;\n-\tstruct resource *res;\n-\tvoid *addr;\n \tunsigned long pfn_last, pfn_first;\n \n \tsize = kvmppc_get_secmem_size();\n@@ -1174,27 +1172,18 @@ int kvmppc_uvmem_init(void)\n \t\tgoto out;\n \t}\n \n-\tres = request_free_mem_region(&iomem_resource, size, \"kvmppc_uvmem\");\n-\tif (IS_ERR(res)) {\n-\t\tret = PTR_ERR(res);\n-\t\tgoto out;\n-\t}\n-\n \tkvmppc_uvmem_pgmap.type = MEMORY_DEVICE_PRIVATE;\n-\tkvmppc_uvmem_pgmap.range.start = res->start;\n-\tkvmppc_uvmem_pgmap.range.end = res->end;\n \tkvmppc_uvmem_pgmap.nr_range = 1;\n+\tkvmppc_uvmem_pgmap.nr_pages = size / PAGE_SIZE;\n \tkvmppc_uvmem_pgmap.ops = &kvmppc_uvmem_ops;\n \t/* just one global instance: */\n \tkvmppc_uvmem_pgmap.owner = &kvmppc_uvmem_pgmap;\n-\taddr = memremap_pages(&kvmppc_uvmem_pgmap, NUMA_NO_NODE);\n-\tif (IS_ERR(addr)) {\n-\t\tret = PTR_ERR(addr);\n-\t\tgoto out_free_region;\n-\t}\n+\tret = memremap_device_private_pagemap(&kvmppc_uvmem_pgmap, NUMA_NO_NODE);\n+\tif (ret)\n+\t\tgoto out;\n \n-\tpfn_first = res->start >> PAGE_SHIFT;\n-\tpfn_last = pfn_first + (resource_size(res) >> PAGE_SHIFT);\n+\tpfn_first = kvmppc_uvmem_pgmap.range.start >> PAGE_SHIFT;\n+\tpfn_last = pfn_first + (range_len(&kvmppc_uvmem_pgmap.range) >> PAGE_SHIFT);\n \tkvmppc_uvmem_bitmap = bitmap_zalloc(pfn_last - pfn_first, GFP_KERNEL);\n \tif (!kvmppc_uvmem_bitmap) {\n \t\tret = -ENOMEM;\n@@ -1204,9 +1193,7 @@ int kvmppc_uvmem_init(void)\n \tpr_info(\"KVMPPC-UVMEM: Secure Memory size 0x%lx\\n\", size);\n \treturn ret;\n out_unmap:\n-\tmemunmap_pages(&kvmppc_uvmem_pgmap);\n-out_free_region:\n-\trelease_mem_region(res->start, size);\n+\tmemunmap_device_private_pagemap(&kvmppc_uvmem_pgmap);\n out:\n \treturn ret;\n }\n@@ -1216,8 +1203,6 @@ void kvmppc_uvmem_free(void)\n \tif (!kvmppc_uvmem_bitmap)\n \t\treturn;\n \n-\tmemunmap_pages(&kvmppc_uvmem_pgmap);\n-\trelease_mem_region(kvmppc_uvmem_pgmap.range.start,\n-\t\t\t range_len(&kvmppc_uvmem_pgmap.range));\n+\tmemunmap_device_private_pagemap(&kvmppc_uvmem_pgmap);\n \tbitmap_free(kvmppc_uvmem_bitmap);\n }\ndiff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c\nindex 1a07a8b92e8f..1e7768b91d9f 100644\n--- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c\n+++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c\n@@ -1024,9 +1024,9 @@ int kgd2kfd_init_zone_device(struct amdgpu_device *adev)\n {\n \tstruct amdgpu_kfd_dev *kfddev = &adev->kfd;\n \tstruct dev_pagemap *pgmap;\n-\tstruct resource *res = NULL;\n \tunsigned long size;\n \tvoid *r;\n+\tint ret;\n \n \t/* Page migration works on gfx9 or newer */\n \tif (amdgpu_ip_version(adev, GC_HWIP, 0) < IP_VERSION(9, 0, 1))\n@@ -1047,11 +1047,7 @@ int kgd2kfd_init_zone_device(struct amdgpu_device *adev)\n \t\tpgmap->range.end = adev->gmc.aper_base + adev->gmc.aper_size - 1;\n \t\tpgmap->type = MEMORY_DEVICE_COHERENT;\n \t} else {\n-\t\tres = devm_request_free_mem_region(adev->dev, &iomem_resource, size);\n-\t\tif (IS_ERR(res))\n-\t\t\treturn PTR_ERR(res);\n-\t\tpgmap->range.start = res->start;\n-\t\tpgmap->range.end = res->end;\n+\t\tpgmap->nr_pages = size / PAGE_SIZE;\n \t\tpgmap->type = MEMORY_DEVICE_PRIVATE;\n \t}\n \n@@ -1062,14 +1058,19 @@ int kgd2kfd_init_zone_device(struct amdgpu_device *adev)\n \t/* Device manager releases device-specific resources, memory region and\n \t * pgmap when driver disconnects from device.\n \t */\n-\tr = devm_memremap_pages(adev->dev, pgmap);\n-\tif (IS_ERR(r)) {\n+\tif (pgmap->type == MEMORY_DEVICE_PRIVATE) {\n+\t\tret = devm_memremap_device_private_pagemap(adev->dev, pgmap);\n+\t} else {\n+\t\tr = devm_memremap_pages(adev->dev, pgmap);\n+\t\tif (IS_ERR(r))\n+\t\t\tret = PTR_ERR(r);\n+\t}\n+\n+\tif (ret) {\n \t\tpr_err(\"failed to register HMM device memory\\n\");\n-\t\tif (pgmap->type == MEMORY_DEVICE_PRIVATE)\n-\t\t\tdevm_release_mem_region(adev->dev, res->start, resource_size(res));\n \t\t/* Disable SVM support capability */\n \t\tpgmap->type = 0;\n-\t\treturn PTR_ERR(r);\n+\t\treturn ret;\n \t}\n \n \tpr_debug(\"reserve %ldMB system memory for VRAM pages struct\\n\",\ndiff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c\nindex adfa3df5cbc5..37fe1cfba414 100644\n--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c\n+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c\n@@ -109,7 +109,7 @@ static struct nouveau_drm *page_to_drm(struct page *page)\n unsigned long nouveau_dmem_page_addr(struct page *page)\n {\n \tstruct nouveau_dmem_chunk *chunk = nouveau_page_to_chunk(page);\n-\tunsigned long off = (page_to_pfn(page) << PAGE_SHIFT) -\n+\tunsigned long off = (device_private_page_to_offset(page) << PAGE_SHIFT) -\n \t\t\t\tchunk->pagemap.range.start;\n \n \treturn chunk->bo->offset + off;\n@@ -297,9 +297,7 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,\n \t\t\t bool is_large)\n {\n \tstruct nouveau_dmem_chunk *chunk;\n-\tstruct resource *res;\n \tstruct page *page;\n-\tvoid *ptr;\n \tunsigned long i, pfn_first, pfn;\n \tint ret;\n \n@@ -309,39 +307,28 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,\n \t\tgoto out;\n \t}\n \n-\t/* Allocate unused physical address space for device private pages. */\n-\tres = request_free_mem_region(&iomem_resource, DMEM_CHUNK_SIZE * NR_CHUNKS,\n-\t\t\t\t \"nouveau_dmem\");\n-\tif (IS_ERR(res)) {\n-\t\tret = PTR_ERR(res);\n-\t\tgoto out_free;\n-\t}\n-\n \tchunk->drm = drm;\n \tchunk->pagemap.type = MEMORY_DEVICE_PRIVATE;\n-\tchunk->pagemap.range.start = res->start;\n-\tchunk->pagemap.range.end = res->end;\n \tchunk->pagemap.nr_range = 1;\n+\tchunk->pagemap.nr_pages = DMEM_CHUNK_SIZE * NR_CHUNKS / PAGE_SIZE;\n \tchunk->pagemap.ops = &nouveau_dmem_pagemap_ops;\n \tchunk->pagemap.owner = drm->dev;\n \n \tret = nouveau_bo_new_pin(&drm->client, NOUVEAU_GEM_DOMAIN_VRAM, DMEM_CHUNK_SIZE,\n \t\t\t\t &chunk->bo);\n \tif (ret)\n-\t\tgoto out_release;\n+\t\tgoto out_free;\n \n-\tptr = memremap_pages(&chunk->pagemap, numa_node_id());\n-\tif (IS_ERR(ptr)) {\n-\t\tret = PTR_ERR(ptr);\n+\tret = memremap_device_private_pagemap(&chunk->pagemap, numa_node_id());\n+\tif (ret)\n \t\tgoto out_bo_free;\n-\t}\n \n \tmutex_lock(&drm->dmem->mutex);\n \tlist_add(&chunk->list, &drm->dmem->chunks);\n \tmutex_unlock(&drm->dmem->mutex);\n \n \tpfn_first = chunk->pagemap.range.start >> PAGE_SHIFT;\n-\tpage = pfn_to_page(pfn_first);\n+\tpage = device_private_offset_to_page(pfn_first);\n \tspin_lock(&drm->dmem->lock);\n \n \tpfn = pfn_first;\n@@ -350,12 +337,12 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,\n \n \t\tif (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) || !is_large) {\n \t\t\tfor (j = 0; j < DMEM_CHUNK_NPAGES - 1; j++, pfn++) {\n-\t\t\t\tpage = pfn_to_page(pfn);\n+\t\t\t\tpage = device_private_offset_to_page(pfn);\n \t\t\t\tpage->zone_device_data = drm->dmem->free_pages;\n \t\t\t\tdrm->dmem->free_pages = page;\n \t\t\t}\n \t\t} else {\n-\t\t\tpage = pfn_to_page(pfn);\n+\t\t\tpage = device_private_offset_to_page(pfn);\n \t\t\tpage->zone_device_data = drm->dmem->free_folios;\n \t\t\tdrm->dmem->free_folios = page_folio(page);\n \t\t\tpfn += DMEM_CHUNK_NPAGES;\n@@ -382,8 +369,6 @@ nouveau_dmem_chunk_alloc(struct nouveau_drm *drm, struct page **ppage,\n \n out_bo_free:\n \tnouveau_bo_unpin_del(&chunk->bo);\n-out_release:\n-\trelease_mem_region(chunk->pagemap.range.start, range_len(&chunk->pagemap.range));\n out_free:\n \tkfree(chunk);\n out:\n@@ -543,9 +528,7 @@ nouveau_dmem_fini(struct nouveau_drm *drm)\n \t\tnouveau_bo_unpin_del(&chunk->bo);\n \t\tWARN_ON(chunk->callocated);\n \t\tlist_del(&chunk->list);\n-\t\tmemunmap_pages(&chunk->pagemap);\n-\t\trelease_mem_region(chunk->pagemap.range.start,\n-\t\t\t\t range_len(&chunk->pagemap.range));\n+\t\tmemunmap_device_private_pagemap(&chunk->pagemap);\n \t\tkfree(chunk);\n \t}\n \ndiff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c\nindex f82790d7e7e6..cce7b4fc5db9 100644\n--- a/drivers/gpu/drm/xe/xe_svm.c\n+++ b/drivers/gpu/drm/xe/xe_svm.c\n@@ -404,7 +404,7 @@ static u64 xe_vram_region_page_to_dpa(struct xe_vram_region *vr,\n \t\t\t\t struct page *page)\n {\n \tu64 dpa;\n-\tu64 pfn = page_to_pfn(page);\n+\tu64 pfn = device_private_page_to_offset(page);\n \tu64 offset;\n \n \txe_assert(vr->xe, is_device_private_page(page));\n@@ -1471,39 +1471,27 @@ int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)\n {\n \tstruct xe_device *xe = tile_to_xe(tile);\n \tstruct device *dev = &to_pci_dev(xe->drm.dev)->dev;\n-\tstruct resource *res;\n-\tvoid *addr;\n \tint ret;\n \n-\tres = devm_request_free_mem_region(dev, &iomem_resource,\n-\t\t\t\t\t vr->usable_size);\n-\tif (IS_ERR(res)) {\n-\t\tret = PTR_ERR(res);\n-\t\treturn ret;\n-\t}\n-\n \tvr->pagemap.type = MEMORY_DEVICE_PRIVATE;\n-\tvr->pagemap.range.start = res->start;\n-\tvr->pagemap.range.end = res->end;\n \tvr->pagemap.nr_range = 1;\n+\tvr->pagemap.nr_pages = vr->usable_size / PAGE_SIZE;\n \tvr->pagemap.ops = drm_pagemap_pagemap_ops_get();\n \tvr->pagemap.owner = xe_svm_devm_owner(xe);\n-\taddr = devm_memremap_pages(dev, &vr->pagemap);\n+\tret = devm_memremap_device_private_pagemap(dev, &vr->pagemap);\n \n \tvr->dpagemap.dev = dev;\n \tvr->dpagemap.ops = &xe_drm_pagemap_ops;\n \n-\tif (IS_ERR(addr)) {\n-\t\tdevm_release_mem_region(dev, res->start, resource_size(res));\n-\t\tret = PTR_ERR(addr);\n-\t\tdrm_err(&xe->drm, \"Failed to remap tile %d memory, errno %pe\\n\",\n-\t\t\ttile->id, ERR_PTR(ret));\n+\tif (ret) {\n+\t\tdrm_err(&xe->drm, \"Failed to remap tile %d memory, errno %d\\n\",\n+\t\t\ttile->id, ret);\n \t\treturn ret;\n \t}\n-\tvr->hpa_base = res->start;\n+\tvr->hpa_base = vr->pagemap.range.start;\n \n \tdrm_dbg(&xe->drm, \"Added tile %d memory [%llx-%llx] to devm, remapped to %pr\\n\",\n-\t\ttile->id, vr->io_start, vr->io_start + vr->usable_size, res);\n+\t\ttile->id, vr->io_start, vr->io_start + vr->usable_size, &vr->pagemap.range);\n \treturn 0;\n }\n #else\ndiff --git a/include/linux/hmm.h b/include/linux/hmm.h\nindex d8756c341620..25bb4df298f7 100644\n--- a/include/linux/hmm.h\n+++ b/include/linux/hmm.h\n@@ -68,6 +68,9 @@ enum hmm_pfn_flags {\n */\n static inline struct page *hmm_pfn_to_page(unsigned long hmm_pfn)\n {\n+\tif (hmm_pfn & HMM_PFN_DEVICE_PRIVATE)\n+\t\treturn device_private_offset_to_page(hmm_pfn & ~HMM_PFN_FLAGS);\n+\n \treturn pfn_to_page(hmm_pfn & ~HMM_PFN_FLAGS);\n }\n \ndiff --git a/include/linux/leafops.h b/include/linux/leafops.h\nindex 2fa09ffe9e34..5c315af273a5 100644\n--- a/include/linux/leafops.h\n+++ b/include/linux/leafops.h\n@@ -455,7 +455,13 @@ static inline unsigned long softleaf_to_flags(softleaf_t entry)\n */\n static inline struct page *softleaf_to_page(softleaf_t entry)\n {\n-\tstruct page *page = pfn_to_page(softleaf_to_pfn(entry));\n+\tstruct page *page;\n+\n+\tif (softleaf_is_migration_device_private(entry) ||\n+\t softleaf_is_device_private(entry))\n+\t\tpage = device_private_entry_to_page(entry);\n+\telse\n+\t\tpage = pfn_to_page(softleaf_to_pfn(entry));\n \n \tVM_WARN_ON_ONCE(!softleaf_has_pfn(entry));\n \t/*\n@@ -475,7 +481,13 @@ static inline struct page *softleaf_to_page(softleaf_t entry)\n */\n static inline struct folio *softleaf_to_folio(softleaf_t entry)\n {\n-\tstruct folio *folio = pfn_folio(softleaf_to_pfn(entry));\n+\tstruct folio *folio;\n+\n+\tif (softleaf_is_migration_device_private(entry) ||\n+\t softleaf_is_device_private(entry))\n+\t\tfolio = page_folio(device_private_entry_to_page(entry));\n+\telse\n+\t\tfolio = pfn_folio(softleaf_to_pfn(entry));\n \n \tVM_WARN_ON_ONCE(!softleaf_has_pfn(entry));\n \t/*\ndiff --git a/include/linux/memremap.h b/include/linux/memremap.h\nindex 713ec0435b48..7fad53f0f6ba 100644\n--- a/include/linux/memremap.h\n+++ b/include/linux/memremap.h\n@@ -37,6 +37,7 @@ struct vmem_altmap {\n * backing the device memory. Doing so simplifies the implementation, but it is\n * important to remember that there are certain points at which the struct page\n * must be treated as an opaque object, rather than a \"normal\" struct page.\n+ * Unlike \"normal\" struct pages, the page_to_pfn() is invalid.\n *\n * A more complete discussion of unaddressable memory may be found in\n * include/linux/hmm.h and Documentation/mm/hmm.rst.\n@@ -126,8 +127,12 @@ struct dev_pagemap_ops {\n * @owner: an opaque pointer identifying the entity that manages this\n *\tinstance. Used by various helpers to make sure that no\n *\tforeign ZONE_DEVICE memory is accessed.\n- * @nr_range: number of ranges to be mapped\n- * @range: range to be mapped when nr_range == 1\n+ * @nr_pages: number of pages requested to be mapped for MEMORY_DEVICE_PRIVATE.\n+ * @pages: array of nr_pages initialized for MEMORY_DEVICE_PRIVATE.\n+ * @nr_range: number of ranges to be mapped. Always == 1 for\n+ *\tMEMORY_DEVICE_PRIVATE.\n+ * @range: range to be mapped when nr_range == 1. Used as an output param for\n+ *\tMEMORY_DEVICE_PRIVATE.\n * @ranges: array of ranges to be mapped when nr_range > 1\n */\n struct dev_pagemap {\n@@ -139,6 +144,8 @@ struct dev_pagemap {\n \tunsigned long vmemmap_shift;\n \tconst struct dev_pagemap_ops *ops;\n \tvoid *owner;\n+\tunsigned long nr_pages;\n+\tstruct page *pages;\n \tint nr_range;\n \tunion {\n \t\tstruct range range;\n@@ -224,7 +231,14 @@ static inline bool is_fsdax_page(const struct page *page)\n }\n \n #ifdef CONFIG_ZONE_DEVICE\n+void __init_zone_device_page(struct page *page, unsigned long pfn,\n+\tunsigned long zone_idx, int nid,\n+\tstruct dev_pagemap *pgmap);\n void zone_device_page_init(struct page *page, unsigned int order);\n+unsigned long memremap_device_private_pagemap(struct dev_pagemap *pgmap, int nid);\n+void memunmap_device_private_pagemap(struct dev_pagemap *pgmap);\n+int devm_memremap_device_private_pagemap(struct device *dev, struct dev_pagemap *pgmap);\n+void devm_memunmap_device_private_pagemap(struct device *dev, struct dev_pagemap *pgmap);\n void *memremap_pages(struct dev_pagemap *pgmap, int nid);\n void memunmap_pages(struct dev_pagemap *pgmap);\n void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap);\n@@ -234,6 +248,15 @@ bool pgmap_pfn_valid(struct dev_pagemap *pgmap, unsigned long pfn);\n \n unsigned long memremap_compat_align(void);\n \n+struct page *device_private_offset_to_page(unsigned long offset);\n+struct page *device_private_entry_to_page(softleaf_t entry);\n+pgoff_t device_private_page_to_offset(const struct page *page);\n+\n+static inline pgoff_t device_private_folio_to_offset(const struct folio *folio)\n+{\n+\treturn device_private_page_to_offset((const struct page *)&folio->page);\n+}\n+\n static inline void zone_device_folio_init(struct folio *folio, unsigned int order)\n {\n \tzone_device_page_init(&folio->page, order);\n@@ -276,6 +299,23 @@ static inline void devm_memunmap_pages(struct device *dev,\n {\n }\n \n+static inline int devm_memremap_device_private_pagemap(struct device *dev,\n+\t\tstruct dev_pagemap *pgmap)\n+{\n+\t/*\n+\t * Fail attempts to call devm_memremap_device_private_pagemap() without\n+\t * ZONE_DEVICE support enabled, this requires callers to fall\n+\t * back to plain devm_memremap() based on config\n+\t */\n+\tWARN_ON_ONCE(1);\n+\treturn -ENXIO;\n+}\n+\n+static inline void devm_memunmap_device_private_pagemap(struct device *dev,\n+\t\tstruct dev_pagemap *pgmap)\n+{\n+}\n+\n static inline struct dev_pagemap *get_dev_pagemap(unsigned long pfn)\n {\n \treturn NULL;\n@@ -296,6 +336,26 @@ static inline void zone_device_private_split_cb(struct folio *original_folio,\n \t\t\t\t\t\tstruct folio *new_folio)\n {\n }\n+\n+static inline struct page *device_private_offset_to_page(unsigned long offset)\n+{\n+\treturn NULL;\n+}\n+\n+static inline struct page *device_private_entry_to_page(softleaf_t entry)\n+{\n+\treturn NULL;\n+}\n+\n+static inline pgoff_t device_private_page_to_offset(const struct page *page)\n+{\n+\treturn 0;\n+}\n+\n+static inline pgoff_t device_private_folio_to_offset(const struct folio *folio)\n+{\n+\treturn 0;\n+}\n #endif /* CONFIG_ZONE_DEVICE */\n \n static inline void put_dev_pagemap(struct dev_pagemap *pgmap)\ndiff --git a/include/linux/migrate.h b/include/linux/migrate.h\nindex 5fd2ee080bc0..2921b3abddf3 100644\n--- a/include/linux/migrate.h\n+++ b/include/linux/migrate.h\n@@ -133,6 +133,10 @@ static inline struct page *migrate_pfn_to_page(unsigned long mpfn)\n {\n \tif (!(mpfn & MIGRATE_PFN_VALID))\n \t\treturn NULL;\n+\n+\tif (mpfn & MIGRATE_PFN_DEVICE_PRIVATE)\n+\t\treturn device_private_offset_to_page(mpfn >> MIGRATE_PFN_SHIFT);\n+\n \treturn pfn_to_page(mpfn >> MIGRATE_PFN_SHIFT);\n }\n \n@@ -144,7 +148,7 @@ static inline unsigned long migrate_pfn(unsigned long pfn)\n static inline unsigned long migrate_pfn_from_page(struct page *page)\n {\n \tif (is_device_private_page(page))\n-\t\treturn migrate_pfn(page_to_pfn(page)) |\n+\t\treturn migrate_pfn(device_private_page_to_offset(page)) |\n \t\t MIGRATE_PFN_DEVICE_PRIVATE;\n \treturn migrate_pfn(page_to_pfn(page));\n }\ndiff --git a/include/linux/mm.h b/include/linux/mm.h\nindex e65329e1969f..b36599ab41ba 100644\n--- a/include/linux/mm.h\n+++ b/include/linux/mm.h\n@@ -2038,6 +2038,8 @@ static inline unsigned long memdesc_section(memdesc_flags_t mdf)\n */\n static inline unsigned long folio_pfn(const struct folio *folio)\n {\n+\tVM_BUG_ON(folio_is_device_private(folio));\n+\n \treturn page_to_pfn(&folio->page);\n }\n \ndiff --git a/include/linux/rmap.h b/include/linux/rmap.h\nindex 57c63b6a8f65..c1561a92864f 100644\n--- a/include/linux/rmap.h\n+++ b/include/linux/rmap.h\n@@ -951,7 +951,7 @@ static inline unsigned long page_vma_walk_pfn(unsigned long pfn)\n static inline unsigned long folio_page_vma_walk_pfn(const struct folio *folio)\n {\n \tif (folio_is_device_private(folio))\n-\t\treturn page_vma_walk_pfn(folio_pfn(folio)) |\n+\t\treturn page_vma_walk_pfn(device_private_folio_to_offset(folio)) |\n \t\t PVMW_PFN_DEVICE_PRIVATE;\n \n \treturn page_vma_walk_pfn(folio_pfn(folio));\n@@ -959,6 +959,9 @@ static inline unsigned long folio_page_vma_walk_pfn(const struct folio *folio)\n \n static inline struct page *page_vma_walk_pfn_to_page(unsigned long pvmw_pfn)\n {\n+\tif (pvmw_pfn & PVMW_PFN_DEVICE_PRIVATE)\n+\t\treturn device_private_offset_to_page(pvmw_pfn >> PVMW_PFN_SHIFT);\n+\n \treturn pfn_to_page(pvmw_pfn >> PVMW_PFN_SHIFT);\n }\n \ndiff --git a/include/linux/swapops.h b/include/linux/swapops.h\nindex f7d85a451a2b..def1d079d69b 100644\n--- a/include/linux/swapops.h\n+++ b/include/linux/swapops.h\n@@ -141,7 +141,7 @@ static inline swp_entry_t make_readable_device_private_entry(pgoff_t offset)\n static inline swp_entry_t make_readable_device_private_entry_from_page(struct page *page,\n \t\t\t\t\t\t\t\t pgoff_t flags)\n {\n-\treturn swp_entry(SWP_DEVICE_READ, page_to_pfn(page) | flags);\n+\treturn swp_entry(SWP_DEVICE_READ, device_private_page_to_offset(page) | flags);\n }\n \n static inline swp_entry_t make_writable_device_private_entry(pgoff_t offset)\n@@ -152,7 +152,7 @@ static inline swp_entry_t make_writable_device_private_entry(pgoff_t offset)\n static inline swp_entry_t make_writable_device_private_entry_from_page(struct page *page,\n \t\t\t\t\t\t\t\t pgoff_t flags)\n {\n-\treturn swp_entry(SWP_DEVICE_WRITE, page_to_pfn(page) | flags);\n+\treturn swp_entry(SWP_DEVICE_WRITE, device_private_page_to_offset(page) | flags);\n }\n \n static inline swp_entry_t make_device_exclusive_entry(pgoff_t offset)\n@@ -268,7 +268,7 @@ static inline swp_entry_t make_readable_migration_entry_from_page(struct page *p\n {\n \tif (is_device_private_page(page))\n \t\treturn make_readable_migration_device_private_entry(\n-\t\t\t\tpage_to_pfn(page) | flags);\n+\t\t\t\tdevice_private_page_to_offset(page) | flags);\n \n \treturn swp_entry(SWP_MIGRATION_READ, page_to_pfn(page) | flags);\n }\n@@ -283,7 +283,7 @@ static inline swp_entry_t make_readable_exclusive_migration_entry_from_page(stru\n {\n \tif (is_device_private_page(page))\n \t\treturn make_readable_exclusive_migration_device_private_entry(\n-\t\t\t\tpage_to_pfn(page) | flags);\n+\t\t\t\tdevice_private_page_to_offset(page) | flags);\n \n \treturn swp_entry(SWP_MIGRATION_READ_EXCLUSIVE, page_to_pfn(page) | flags);\n }\n@@ -298,7 +298,7 @@ static inline swp_entry_t make_writable_migration_entry_from_page(struct page *p\n {\n \tif (is_device_private_page(page))\n \t\treturn make_writable_migration_device_private_entry(\n-\t\t\t\tpage_to_pfn(page) | flags);\n+\t\t\t\tdevice_private_page_to_offset(page) | flags);\n \n \treturn swp_entry(SWP_MIGRATION_WRITE, page_to_pfn(page) | flags);\n }\ndiff --git a/lib/test_hmm.c b/lib/test_hmm.c\nindex 872d3846af7b..b6e20041e448 100644\n--- a/lib/test_hmm.c\n+++ b/lib/test_hmm.c\n@@ -497,7 +497,7 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \t\t\t\t struct page **ppage, bool is_large)\n {\n \tstruct dmirror_chunk *devmem;\n-\tstruct resource *res = NULL;\n+\tbool device_private = false;\n \tunsigned long pfn;\n \tunsigned long pfn_first;\n \tunsigned long pfn_last;\n@@ -510,13 +510,9 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \n \tswitch (mdevice->zone_device_type) {\n \tcase HMM_DMIRROR_MEMORY_DEVICE_PRIVATE:\n-\t\tres = request_free_mem_region(&iomem_resource, DEVMEM_CHUNK_SIZE,\n-\t\t\t\t\t \"hmm_dmirror\");\n-\t\tif (IS_ERR_OR_NULL(res))\n-\t\t\tgoto err_devmem;\n-\t\tdevmem->pagemap.range.start = res->start;\n-\t\tdevmem->pagemap.range.end = res->end;\n+\t\tdevice_private = true;\n \t\tdevmem->pagemap.type = MEMORY_DEVICE_PRIVATE;\n+\t\tdevmem->pagemap.nr_pages = DEVMEM_CHUNK_SIZE / PAGE_SIZE;\n \t\tbreak;\n \tcase HMM_DMIRROR_MEMORY_DEVICE_COHERENT:\n \t\tdevmem->pagemap.range.start = (MINOR(mdevice->cdevice.dev) - 2) ?\n@@ -525,13 +521,13 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \t\tdevmem->pagemap.range.end = devmem->pagemap.range.start +\n \t\t\t\t\t DEVMEM_CHUNK_SIZE - 1;\n \t\tdevmem->pagemap.type = MEMORY_DEVICE_COHERENT;\n+\t\tdevmem->pagemap.nr_range = 1;\n \t\tbreak;\n \tdefault:\n \t\tret = -EINVAL;\n \t\tgoto err_devmem;\n \t}\n \n-\tdevmem->pagemap.nr_range = 1;\n \tdevmem->pagemap.ops = &dmirror_devmem_ops;\n \tdevmem->pagemap.owner = mdevice;\n \n@@ -551,13 +547,20 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \t\tmdevice->devmem_capacity = new_capacity;\n \t\tmdevice->devmem_chunks = new_chunks;\n \t}\n-\tptr = memremap_pages(&devmem->pagemap, numa_node_id());\n-\tif (IS_ERR_OR_NULL(ptr)) {\n-\t\tif (ptr)\n-\t\t\tret = PTR_ERR(ptr);\n-\t\telse\n-\t\t\tret = -EFAULT;\n-\t\tgoto err_release;\n+\n+\tif (device_private) {\n+\t\tret = memremap_device_private_pagemap(&devmem->pagemap, numa_node_id());\n+\t\tif (ret)\n+\t\t\tgoto err_release;\n+\t} else {\n+\t\tptr = memremap_pages(&devmem->pagemap, numa_node_id());\n+\t\tif (IS_ERR_OR_NULL(ptr)) {\n+\t\t\tif (ptr)\n+\t\t\t\tret = PTR_ERR(ptr);\n+\t\t\telse\n+\t\t\t\tret = -EFAULT;\n+\t\t\tgoto err_release;\n+\t\t}\n \t}\n \n \tdevmem->mdevice = mdevice;\n@@ -567,15 +570,21 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \n \tmutex_unlock(&mdevice->devmem_lock);\n \n-\tpr_info(\"added new %u MB chunk (total %u chunks, %u MB) PFNs [0x%lx 0x%lx)\\n\",\n+\tpr_info(\"added new %u MB chunk (total %u chunks, %u MB) %sPFNs [0x%lx 0x%lx)\\n\",\n \t\tDEVMEM_CHUNK_SIZE / (1024 * 1024),\n \t\tmdevice->devmem_count,\n \t\tmdevice->devmem_count * (DEVMEM_CHUNK_SIZE / (1024 * 1024)),\n+\t\tdevice_private ? \"device \" : \"\",\n \t\tpfn_first, pfn_last);\n \n \tspin_lock(&mdevice->lock);\n \tfor (pfn = pfn_first; pfn < pfn_last; ) {\n-\t\tstruct page *page = pfn_to_page(pfn);\n+\t\tstruct page *page;\n+\n+\t\tif (device_private)\n+\t\t\tpage = device_private_offset_to_page(pfn);\n+\t\telse\n+\t\t\tpage = pfn_to_page(pfn);\n \n \t\tif (is_large && IS_ALIGNED(pfn, HPAGE_PMD_NR)\n \t\t\t&& (pfn + HPAGE_PMD_NR <= pfn_last)) {\n@@ -616,9 +625,6 @@ static int dmirror_allocate_chunk(struct dmirror_device *mdevice,\n \n err_release:\n \tmutex_unlock(&mdevice->devmem_lock);\n-\tif (res && devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)\n-\t\trelease_mem_region(devmem->pagemap.range.start,\n-\t\t\t\t range_len(&devmem->pagemap.range));\n err_devmem:\n \tkfree(devmem);\n \n@@ -696,8 +702,8 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args,\n \t\t */\n \t\tspage = migrate_pfn_to_page(*src);\n \t\tif (WARN(spage && is_zone_device_page(spage),\n-\t\t \"page already in device spage pfn: 0x%lx\\n\",\n-\t\t page_to_pfn(spage)))\n+\t\t \"page already in device spage mpfn: 0x%lx\\n\",\n+\t\t migrate_pfn_from_page(spage)))\n \t\t\tgoto next;\n \n \t\tif (dmirror->flags & HMM_DMIRROR_FLAG_FAIL_ALLOC) {\n@@ -752,8 +758,9 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args,\n \t\t */\n \t\trpage->zone_device_data = dmirror;\n \n-\t\tpr_debug(\"migrating from sys to dev pfn src: 0x%lx pfn dst: 0x%lx\\n\",\n-\t\t\t page_to_pfn(spage), page_to_pfn(dpage));\n+\t\tpr_debug(\"migrating from sys to mpfn src: 0x%lx pfn dst: 0x%lx\\n\",\n+\t\t\t page_to_pfn(spage),\n+\t\t\t migrate_pfn_from_page(dpage));\n \n \t\t*dst = migrate_pfn_from_page(dpage) | write;\n \n@@ -1462,10 +1469,10 @@ static void dmirror_device_remove_chunks(struct dmirror_device *mdevice)\n \t\t\tspin_unlock(&mdevice->lock);\n \n \t\t\tdmirror_device_evict_chunk(devmem);\n-\t\t\tmemunmap_pages(&devmem->pagemap);\n \t\t\tif (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)\n-\t\t\t\trelease_mem_region(devmem->pagemap.range.start,\n-\t\t\t\t\t\t range_len(&devmem->pagemap.range));\n+\t\t\t\tmemunmap_device_private_pagemap(&devmem->pagemap);\n+\t\t\telse\n+\t\t\t\tmemunmap_pages(&devmem->pagemap);\n \t\t\tkfree(devmem);\n \t\t}\n \t\tmdevice->devmem_count = 0;\n@@ -1710,7 +1717,12 @@ static void dmirror_devmem_folio_split(struct folio *head, struct folio *tail)\n \t\treturn;\n \t}\n \n-\toffset = folio_pfn(tail) - folio_pfn(head);\n+\ttail->pgmap = head->pgmap;\n+\n+\tif (folio_is_device_private(head))\n+\t\toffset = device_private_folio_to_offset(tail) - device_private_folio_to_offset(head);\n+\telse\n+\t\toffset = folio_pfn(tail) - folio_pfn(head);\n \n \trpage_tail = folio_page(rfolio, offset);\n \ttail->page.zone_device_data = rpage_tail;\n@@ -1719,7 +1731,6 @@ static void dmirror_devmem_folio_split(struct folio *head, struct folio *tail)\n \trpage_tail->mapping = NULL;\n \n \tfolio_page(tail, 0)->mapping = folio_page(head, 0)->mapping;\n-\ttail->pgmap = head->pgmap;\n \tfolio_set_count(page_folio(rpage_tail), 1);\n }\n \ndiff --git a/mm/debug.c b/mm/debug.c\nindex 77fa8fe1d641..04fcc62d440f 100644\n--- a/mm/debug.c\n+++ b/mm/debug.c\n@@ -77,9 +77,11 @@ static void __dump_folio(const struct folio *folio, const struct page *page,\n \tif (page_mapcount_is_type(mapcount))\n \t\tmapcount = 0;\n \n-\tpr_warn(\"page: refcount:%d mapcount:%d mapping:%p index:%#lx pfn:%#lx\\n\",\n+\tpr_warn(\"page: refcount:%d mapcount:%d mapping:%p index:%#lx %spfn:%#lx\\n\",\n \t\t\tfolio_ref_count(folio), mapcount, mapping,\n-\t\t\tfolio->index + idx, pfn);\n+\t\t\tfolio->index + idx,\n+\t\t\tfolio_is_device_private(folio) ? \"device \" : \"\",\n+\t\t\tpfn);\n \tif (folio_test_large(folio)) {\n \t\tint pincount = 0;\n \n@@ -113,7 +115,8 @@ static void __dump_folio(const struct folio *folio, const struct page *page,\n \t * inaccuracy here due to racing.\n \t */\n \tpr_warn(\"%sflags: %pGp%s\\n\", type, &folio->flags,\n-\t\tis_migrate_cma_folio(folio, pfn) ? \" CMA\" : \"\");\n+\t\t(!folio_is_device_private(folio) &&\n+\t\t is_migrate_cma_folio(folio, pfn)) ? \" CMA\" : \"\");\n \tif (page_has_type(&folio->page))\n \t\tpr_warn(\"page_type: %x(%s)\\n\", folio->page.page_type >> 24,\n \t\t\t\tpage_type_name(folio->page.page_type));\ndiff --git a/mm/memremap.c b/mm/memremap.c\nindex 4c2e0d68eb27..f0fe92c3227a 100644\n--- a/mm/memremap.c\n+++ b/mm/memremap.c\n@@ -12,9 +12,12 @@\n #include <linux/types.h>\n #include <linux/wait_bit.h>\n #include <linux/xarray.h>\n+#include <linux/maple_tree.h>\n #include \"internal.h\"\n \n static DEFINE_XARRAY(pgmap_array);\n+static struct maple_tree device_private_pgmap_tree =\n+\tMTREE_INIT(device_private_pgmap_tree, MT_FLAGS_ALLOC_RANGE);\n \n /*\n * The memremap() and memremap_pages() interfaces are alternately used\n@@ -113,9 +116,10 @@ void memunmap_pages(struct dev_pagemap *pgmap)\n {\n \tint i;\n \n+\tWARN_ONCE(pgmap->type == MEMORY_DEVICE_PRIVATE, \"Type should not be MEMORY_DEVICE_PRIVATE\\n\");\n+\n \tpercpu_ref_kill(&pgmap->ref);\n-\tif (pgmap->type != MEMORY_DEVICE_PRIVATE &&\n-\t pgmap->type != MEMORY_DEVICE_COHERENT)\n+\tif (pgmap->type != MEMORY_DEVICE_COHERENT)\n \t\tfor (i = 0; i < pgmap->nr_range; i++)\n \t\t\tpercpu_ref_put_many(&pgmap->ref, pfn_len(pgmap, i));\n \n@@ -144,7 +148,6 @@ static void dev_pagemap_percpu_release(struct percpu_ref *ref)\n static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,\n \t\tint range_id, int nid)\n {\n-\tconst bool is_private = pgmap->type == MEMORY_DEVICE_PRIVATE;\n \tstruct range *range = &pgmap->ranges[range_id];\n \tstruct dev_pagemap *conflict_pgmap;\n \tint error, is_ram;\n@@ -190,7 +193,7 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,\n \tif (error)\n \t\tgoto err_pfn_remap;\n \n-\tif (!mhp_range_allowed(range->start, range_len(range), !is_private)) {\n+\tif (!mhp_range_allowed(range->start, range_len(range), true)) {\n \t\terror = -EINVAL;\n \t\tgoto err_kasan;\n \t}\n@@ -198,30 +201,19 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,\n \tmem_hotplug_begin();\n \n \t/*\n-\t * For device private memory we call add_pages() as we only need to\n-\t * allocate and initialize struct page for the device memory. More-\n-\t * over the device memory is un-accessible thus we do not want to\n-\t * create a linear mapping for the memory like arch_add_memory()\n-\t * would do.\n-\t *\n-\t * For all other device memory types, which are accessible by\n-\t * the CPU, we do want the linear mapping and thus use\n+\t * All device memory types except device private memory are accessible\n+\t * by the CPU, so we want the linear mapping and thus use\n \t * arch_add_memory().\n \t */\n-\tif (is_private) {\n-\t\terror = add_pages(nid, PHYS_PFN(range->start),\n-\t\t\t\tPHYS_PFN(range_len(range)), params);\n-\t} else {\n-\t\terror = kasan_add_zero_shadow(__va(range->start), range_len(range));\n-\t\tif (error) {\n-\t\t\tmem_hotplug_done();\n-\t\t\tgoto err_kasan;\n-\t\t}\n-\n-\t\terror = arch_add_memory(nid, range->start, range_len(range),\n-\t\t\t\t\tparams);\n+\terror = kasan_add_zero_shadow(__va(range->start), range_len(range));\n+\tif (error) {\n+\t\tmem_hotplug_done();\n+\t\tgoto err_kasan;\n \t}\n \n+\terror = arch_add_memory(nid, range->start, range_len(range),\n+\t\t\t\tparams);\n+\n \tif (!error) {\n \t\tstruct zone *zone;\n \n@@ -248,8 +240,7 @@ static int pagemap_range(struct dev_pagemap *pgmap, struct mhp_params *params,\n \treturn 0;\n \n err_add_memory:\n-\tif (!is_private)\n-\t\tkasan_remove_zero_shadow(__va(range->start), range_len(range));\n+\tkasan_remove_zero_shadow(__va(range->start), range_len(range));\n err_kasan:\n \tpfnmap_untrack(PHYS_PFN(range->start), range_len(range));\n err_pfn_remap:\n@@ -281,22 +272,8 @@ void *memremap_pages(struct dev_pagemap *pgmap, int nid)\n \n \tswitch (pgmap->type) {\n \tcase MEMORY_DEVICE_PRIVATE:\n-\t\tif (!IS_ENABLED(CONFIG_DEVICE_PRIVATE)) {\n-\t\t\tWARN(1, \"Device private memory not supported\\n\");\n-\t\t\treturn ERR_PTR(-EINVAL);\n-\t\t}\n-\t\tif (!pgmap->ops || !pgmap->ops->migrate_to_ram) {\n-\t\t\tWARN(1, \"Missing migrate_to_ram method\\n\");\n-\t\t\treturn ERR_PTR(-EINVAL);\n-\t\t}\n-\t\tif (!pgmap->ops->folio_free) {\n-\t\t\tWARN(1, \"Missing folio_free method\\n\");\n-\t\t\treturn ERR_PTR(-EINVAL);\n-\t\t}\n-\t\tif (!pgmap->owner) {\n-\t\t\tWARN(1, \"Missing owner\\n\");\n-\t\t\treturn ERR_PTR(-EINVAL);\n-\t\t}\n+\t\tWARN(1, \"Use memremap_device_private_pagemap()\\n\");\n+\t\treturn ERR_PTR(-EINVAL);\n \t\tbreak;\n \tcase MEMORY_DEVICE_COHERENT:\n \t\tif (!pgmap->ops->folio_free) {\n@@ -394,6 +371,31 @@ void devm_memunmap_pages(struct device *dev, struct dev_pagemap *pgmap)\n }\n EXPORT_SYMBOL_GPL(devm_memunmap_pages);\n \n+static void devm_memremap_device_private_pagemap_release(void *data)\n+{\n+\tmemunmap_device_private_pagemap(data);\n+}\n+\n+int devm_memremap_device_private_pagemap(struct device *dev, struct dev_pagemap *pgmap)\n+{\n+\tint ret;\n+\n+\tret = memremap_device_private_pagemap(pgmap, dev_to_node(dev));\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = devm_add_action_or_reset(dev, devm_memremap_device_private_pagemap_release,\n+\t\t\tpgmap);\n+\treturn ret;\n+}\n+EXPORT_SYMBOL_GPL(devm_memremap_device_private_pagemap);\n+\n+void devm_memunmap_device_private_pagemap(struct device *dev, struct dev_pagemap *pgmap)\n+{\n+\tdevm_release_action(dev, devm_memremap_device_private_pagemap_release, pgmap);\n+}\n+EXPORT_SYMBOL_GPL(devm_memunmap_device_private_pagemap);\n+\n /**\n * get_dev_pagemap() - take a new live reference on the dev_pagemap for @pfn\n * @pfn: page frame number to lookup page_map\n@@ -495,3 +497,110 @@ void zone_device_page_init(struct page *page, unsigned int order)\n \t\tprep_compound_page(page, order);\n }\n EXPORT_SYMBOL_GPL(zone_device_page_init);\n+\n+unsigned long memremap_device_private_pagemap(struct dev_pagemap *pgmap, int nid)\n+{\n+\tunsigned long dpfn, dpfn_first, dpfn_last = 0;\n+\tunsigned long start;\n+\tint rc;\n+\n+\tif (pgmap->type != MEMORY_DEVICE_PRIVATE) {\n+\t\tWARN(1, \"Not device private memory\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\tif (!IS_ENABLED(CONFIG_DEVICE_PRIVATE)) {\n+\t\tWARN(1, \"Device private memory not supported\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\tif (!pgmap->ops || !pgmap->ops->migrate_to_ram) {\n+\t\tWARN(1, \"Missing migrate_to_ram method\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\tif (!pgmap->owner) {\n+\t\tWARN(1, \"Missing owner\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tpgmap->pages = kvzalloc(sizeof(struct page) * pgmap->nr_pages,\n+\t\t\t GFP_KERNEL);\n+\tif (!pgmap->pages)\n+\t\treturn -ENOMEM;\n+\n+\trc = mtree_alloc_range(&device_private_pgmap_tree, &start, pgmap,\n+\t\t\t pgmap->nr_pages * PAGE_SIZE, 0,\n+\t\t\t 1ull << MAX_PHYSMEM_BITS, GFP_KERNEL);\n+\tif (rc < 0)\n+\t\tgoto err_mtree_alloc;\n+\n+\tpgmap->range.start = start;\n+\tpgmap->range.end = pgmap->range.start + (pgmap->nr_pages * PAGE_SIZE) - 1;\n+\tpgmap->nr_range = 1;\n+\n+\tinit_completion(&pgmap->done);\n+\trc = percpu_ref_init(&pgmap->ref, dev_pagemap_percpu_release, 0,\n+\t\tGFP_KERNEL);\n+\tif (rc < 0)\n+\t\tgoto err_ref_init;\n+\n+\tdpfn_first = pgmap->range.start >> PAGE_SHIFT;\n+\tdpfn_last = dpfn_first + (range_len(&pgmap->range) >> PAGE_SHIFT);\n+\tfor (dpfn = dpfn_first; dpfn < dpfn_last; dpfn++) {\n+\t\tstruct page *page = device_private_offset_to_page(dpfn);\n+\n+\t\t__init_zone_device_page(page, dpfn, ZONE_DEVICE, nid, pgmap);\n+\t\tpage_folio(page)->pgmap = (void *) pgmap;\n+\t}\n+\n+\treturn 0;\n+\n+err_ref_init:\n+\tmtree_erase(&device_private_pgmap_tree, pgmap->range.start);\n+err_mtree_alloc:\n+\tkvfree(pgmap->pages);\n+\treturn rc;\n+}\n+EXPORT_SYMBOL_GPL(memremap_device_private_pagemap);\n+\n+void memunmap_device_private_pagemap(struct dev_pagemap *pgmap)\n+{\n+\tpercpu_ref_kill(&pgmap->ref);\n+\twait_for_completion(&pgmap->done);\n+\tpercpu_ref_exit(&pgmap->ref);\n+\tkvfree(pgmap->pages);\n+\tmtree_erase(&device_private_pgmap_tree, pgmap->range.start);\n+}\n+EXPORT_SYMBOL_GPL(memunmap_device_private_pagemap);\n+\n+struct page *device_private_offset_to_page(unsigned long offset)\n+{\n+\tstruct dev_pagemap *pgmap;\n+\n+\tpgmap = mtree_load(&device_private_pgmap_tree, offset << PAGE_SHIFT);\n+\tif (WARN_ON_ONCE(!pgmap))\n+\t\treturn NULL;\n+\n+\treturn &pgmap->pages[offset - (pgmap->range.start >> PAGE_SHIFT)];\n+}\n+EXPORT_SYMBOL_GPL(device_private_offset_to_page);\n+\n+struct page *device_private_entry_to_page(softleaf_t entry)\n+{\n+\tunsigned long offset;\n+\n+\tif (!((softleaf_is_device_private(entry) ||\n+\t (softleaf_is_migration_device_private(entry)))))\n+\t\treturn NULL;\n+\n+\toffset = softleaf_to_pfn(entry);\n+\treturn device_private_offset_to_page(offset);\n+}\n+\n+pgoff_t device_private_page_to_offset(const struct page *page)\n+{\n+\tstruct dev_pagemap *pgmap = (struct dev_pagemap *) page_pgmap(page);\n+\n+\tVM_BUG_ON_PAGE(!is_device_private_page(page), page);\n+\n+\treturn (pgmap->range.start >> PAGE_SHIFT) + ((page - pgmap->pages));\n+}\n+EXPORT_SYMBOL_GPL(device_private_page_to_offset);\ndiff --git a/mm/mm_init.c b/mm/mm_init.c\nindex fc2a6f1e518f..4a9420cb610c 100644\n--- a/mm/mm_init.c\n+++ b/mm/mm_init.c\n@@ -1004,9 +1004,9 @@ static void __init memmap_init(void)\n }\n \n #ifdef CONFIG_ZONE_DEVICE\n-static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,\n-\t\t\t\t\t unsigned long zone_idx, int nid,\n-\t\t\t\t\t struct dev_pagemap *pgmap)\n+void __ref __init_zone_device_page(struct page *page, unsigned long pfn,\n+\t\t\t\t unsigned long zone_idx, int nid,\n+\t\t\t\t struct dev_pagemap *pgmap)\n {\n \n \t__init_single_page(page, pfn, zone_idx, nid);\n@@ -1038,7 +1038,7 @@ static void __ref __init_zone_device_page(struct page *page, unsigned long pfn,\n \t * Please note that MEMINIT_HOTPLUG path doesn't clear memmap\n \t * because this is done early in section_activate()\n \t */\n-\tif (pageblock_aligned(pfn)) {\n+\tif (pgmap->type != MEMORY_DEVICE_PRIVATE && pageblock_aligned(pfn)) {\n \t\tinit_pageblock_migratetype(page, MIGRATE_MOVABLE, false);\n \t\tcond_resched();\n \t}\ndiff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c\nindex 96c525785d78..141fe5abd33f 100644\n--- a/mm/page_vma_mapped.c\n+++ b/mm/page_vma_mapped.c\n@@ -107,6 +107,7 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw, pmd_t *pmdvalp,\n static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)\n {\n \tunsigned long pfn;\n+\tbool device_private = false;\n \tpte_t ptent = ptep_get(pvmw->pte);\n \n \tif (pvmw->flags & PVMW_MIGRATION) {\n@@ -115,6 +116,9 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)\n \t\tif (!softleaf_is_migration(entry))\n \t\t\treturn false;\n \n+\t\tif (softleaf_is_migration_device_private(entry))\n+\t\t\tdevice_private = true;\n+\n \t\tpfn = softleaf_to_pfn(entry);\n \t} else if (pte_present(ptent)) {\n \t\tpfn = pte_pfn(ptent);\n@@ -127,8 +131,14 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)\n \t\t\treturn false;\n \n \t\tpfn = softleaf_to_pfn(entry);\n+\n+\t\tif (softleaf_is_device_private(entry))\n+\t\t\tdevice_private = true;\n \t}\n \n+\tif ((device_private) ^ !!(pvmw->pfn & PVMW_PFN_DEVICE_PRIVATE))\n+\t\treturn false;\n+\n \tif ((pfn + pte_nr - 1) < (pvmw->pfn >> PVMW_PFN_SHIFT))\n \t\treturn false;\n \tif (pfn > ((pvmw->pfn >> PVMW_PFN_SHIFT) + pvmw->nr_pages - 1))\n@@ -137,8 +147,11 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw, unsigned long pte_nr)\n }\n \n /* Returns true if the two ranges overlap. Careful to not overflow. */\n-static bool check_pmd(unsigned long pfn, struct page_vma_mapped_walk *pvmw)\n+static bool check_pmd(unsigned long pfn, bool device_private, struct page_vma_mapped_walk *pvmw)\n {\n+\tif ((device_private) ^ !!(pvmw->pfn & PVMW_PFN_DEVICE_PRIVATE))\n+\t\treturn false;\n+\n \tif ((pfn + HPAGE_PMD_NR - 1) < (pvmw->pfn >> PVMW_PFN_SHIFT))\n \t\treturn false;\n \tif (pfn > (pvmw->pfn >> PVMW_PFN_SHIFT) + pvmw->nr_pages - 1)\n@@ -255,6 +268,8 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)\n \n \t\t\t\tif (!softleaf_is_migration(entry) ||\n \t\t\t\t !check_pmd(softleaf_to_pfn(entry),\n+\t\t\t\t\t softleaf_is_device_private(entry) ||\n+\t\t\t\t\t softleaf_is_migration_device_private(entry),\n \t\t\t\t\t pvmw))\n \t\t\t\t\treturn not_found(pvmw);\n \t\t\t\treturn true;\n@@ -262,7 +277,7 @@ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw)\n \t\t\tif (likely(pmd_trans_huge(pmde))) {\n \t\t\t\tif (pvmw->flags & PVMW_MIGRATION)\n \t\t\t\t\treturn not_found(pvmw);\n-\t\t\t\tif (!check_pmd(pmd_pfn(pmde), pvmw))\n+\t\t\t\tif (!check_pmd(pmd_pfn(pmde), false, pvmw))\n \t\t\t\t\treturn not_found(pvmw);\n \t\t\t\treturn true;\n \t\t\t}\ndiff --git a/mm/rmap.c b/mm/rmap.c\nindex 6a63333f8722..3f708ed5c89f 100644\n--- a/mm/rmap.c\n+++ b/mm/rmap.c\n@@ -1860,7 +1860,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,\n \tstruct mmu_notifier_range range;\n \tenum ttu_flags flags = (enum ttu_flags)(long)arg;\n \tunsigned long nr_pages = 1, end_addr;\n-\tunsigned long pfn;\n+\tunsigned long nr;\n \tunsigned long hsz = 0;\n \tint ptes = 0;\n \n@@ -1967,15 +1967,20 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma,\n \t\t */\n \t\tpteval = ptep_get(pvmw.pte);\n \t\tif (likely(pte_present(pteval))) {\n-\t\t\tpfn = pte_pfn(pteval);\n+\t\t\tnr = pte_pfn(pteval) - folio_pfn(folio);\n \t\t} else {\n \t\t\tconst softleaf_t entry = softleaf_from_pte(pteval);\n \n-\t\t\tpfn = softleaf_to_pfn(entry);\n+\t\t\tif (softleaf_is_device_private(entry) ||\n+\t\t\t softleaf_is_migration_device_private(entry))\n+\t\t\t\tnr = softleaf_to_pfn(entry) - device_private_folio_to_offset(folio);\n+\t\t\telse\n+\t\t\t\tnr = softleaf_to_pfn(entry) - folio_pfn(folio);\n+\n \t\t\tVM_WARN_ON_FOLIO(folio_test_hugetlb(folio), folio);\n \t\t}\n \n-\t\tsubpage = folio_page(folio, pfn - folio_pfn(folio));\n+\t\tsubpage = folio_page(folio, nr);\n \t\taddress = pvmw.address;\n \t\tanon_exclusive = folio_test_anon(folio) &&\n \t\t\t\t PageAnonExclusive(subpage);\n@@ -2289,7 +2294,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,\n \tstruct page *subpage;\n \tstruct mmu_notifier_range range;\n \tenum ttu_flags flags = (enum ttu_flags)(long)arg;\n-\tunsigned long pfn;\n+\tunsigned long nr;\n \tunsigned long hsz = 0;\n \n \t/*\n@@ -2328,7 +2333,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,\n \twhile (page_vma_mapped_walk(&pvmw)) {\n \t\t/* PMD-mapped THP migration entry */\n \t\tif (!pvmw.pte) {\n-\t\t\t__maybe_unused unsigned long pfn;\n+\t\t\t__maybe_unused softleaf_t entry;\n \t\t\t__maybe_unused pmd_t pmdval;\n \n \t\t\tif (flags & TTU_SPLIT_HUGE_PMD) {\n@@ -2340,12 +2345,17 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,\n \t\t\t}\n #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION\n \t\t\tpmdval = pmdp_get(pvmw.pmd);\n+\t\t\tentry = softleaf_from_pmd(pmdval);\n \t\t\tif (likely(pmd_present(pmdval)))\n-\t\t\t\tpfn = pmd_pfn(pmdval);\n-\t\t\telse\n-\t\t\t\tpfn = softleaf_to_pfn(softleaf_from_pmd(pmdval));\n+\t\t\t\tnr = pmd_pfn(pmdval) - folio_pfn(folio);\n+\t\t\telse if (softleaf_is_device_private(entry) ||\n+\t\t\t\t softleaf_is_migration_device_private(entry)) {\n+\t\t\t\tnr = softleaf_to_pfn(entry) - device_private_folio_to_offset(folio);\n+\t\t\t} else {\n+\t\t\t\tnr = softleaf_to_pfn(entry) - folio_pfn(folio);\n+\t\t\t}\n \n-\t\t\tsubpage = folio_page(folio, pfn - folio_pfn(folio));\n+\t\t\tsubpage = folio_page(folio, nr);\n \n \t\t\tVM_BUG_ON_FOLIO(folio_test_hugetlb(folio) ||\n \t\t\t\t\t!folio_test_pmd_mappable(folio), folio);\n@@ -2368,15 +2378,20 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,\n \t\t */\n \t\tpteval = ptep_get(pvmw.pte);\n \t\tif (likely(pte_present(pteval))) {\n-\t\t\tpfn = pte_pfn(pteval);\n+\t\t\tnr = pte_pfn(pteval) - folio_pfn(folio);\n \t\t} else {\n \t\t\tconst softleaf_t entry = softleaf_from_pte(pteval);\n \n-\t\t\tpfn = softleaf_to_pfn(entry);\n+\t\t\tif (softleaf_is_device_private(entry) ||\n+\t\t\t is_device_private_migration_entry(entry))\n+\t\t\t\tnr = softleaf_to_pfn(entry) - device_private_folio_to_offset(folio);\n+\t\t\telse\n+\t\t\t\tnr = softleaf_to_pfn(entry) - folio_pfn(folio);\n+\n \t\t\tVM_WARN_ON_FOLIO(folio_test_hugetlb(folio), folio);\n \t\t}\n \n-\t\tsubpage = folio_page(folio, pfn - folio_pfn(folio));\n+\t\tsubpage = folio_page(folio, nr);\n \t\taddress = pvmw.address;\n \t\tanon_exclusive = folio_test_anon(folio) &&\n \t\t\t\t PageAnonExclusive(subpage);\n@@ -2436,7 +2451,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma,\n \t\t\t\tfolio_mark_dirty(folio);\n \t\t\twritable = pte_write(pteval);\n \t\t} else if (likely(pte_present(pteval))) {\n-\t\t\tflush_cache_page(vma, address, pfn);\n+\t\t\tflush_cache_page(vma, address, pte_pfn(pteval));\n \t\t\t/* Nuke the page table entry. */\n \t\t\tif (should_defer_flush(mm, flags)) {\n \t\t\t\t/*\ndiff --git a/mm/util.c b/mm/util.c\nindex 65e3f1a97d76..8482ebc5c394 100644\n--- a/mm/util.c\n+++ b/mm/util.c\n@@ -1244,7 +1244,10 @@ void snapshot_page(struct page_snapshot *ps, const struct page *page)\n \tstruct folio *foliop;\n \tint loops = 5;\n \n-\tps->pfn = page_to_pfn(page);\n+\tif (is_device_private_page(page))\n+\t\tps->pfn = device_private_page_to_offset(page);\n+\telse\n+\t\tps->pfn = page_to_pfn(page);\n \tps->flags = PAGE_SNAPSHOT_FAITHFUL;\n \n again:\n", "prefixes": [ "v2", "11/11" ] }