{"id":2225778,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2225778/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pci/patch/20260421-io_projection-v2-11-4c251c692ef4@garyguo.net/","project":{"id":28,"url":"http://patchwork.ozlabs.org/api/1.1/projects/28/?format=json","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":"<20260421-io_projection-v2-11-4c251c692ef4@garyguo.net>","date":"2026-04-21T14:56:22","name":"[v2,11/11] rust: io: add copying methods","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"05138167079cddc58451fca09cc72a0b019d69ee","submitter":{"id":76823,"url":"http://patchwork.ozlabs.org/api/1.1/people/76823/?format=json","name":"Gary Guo","email":"gary@garyguo.net"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-pci/patch/20260421-io_projection-v2-11-4c251c692ef4@garyguo.net/mbox/","series":[{"id":500833,"url":"http://patchwork.ozlabs.org/api/1.1/series/500833/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-pci/list/?series=500833","date":"2026-04-21T14:56:19","name":"rust: I/O type generalization and projection","version":2,"mbox":"http://patchwork.ozlabs.org/series/500833/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2225778/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2225778/checks/","tags":{},"headers":{"Return-Path":"\n <linux-pci+bounces-52849-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=garyguo.net header.i=@garyguo.net header.a=rsa-sha256\n header.s=selector1 header.b=rd59Q9yf;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c15:e001:75::12fc:5321; helo=sin.lore.kernel.org;\n envelope-from=linux-pci+bounces-52849-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net\n header.b=\"rd59Q9yf\"","smtp.subspace.kernel.org;\n arc=fail smtp.client-ip=52.101.196.131","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=garyguo.net","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=garyguo.net","dkim=none (message not signed)\n header.d=none;dmarc=none action=none header.from=garyguo.net;"],"Received":["from sin.lore.kernel.org (sin.lore.kernel.org\n [IPv6:2600:3c15:e001:75::12fc:5321])\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 4g0Qdd5XFMz1yGs\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 01:05:25 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sin.lore.kernel.org (Postfix) with ESMTP id 0001B303BAD2\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 14:58:11 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 49E693DCD83;\n\tTue, 21 Apr 2026 14:56:45 +0000 (UTC)","from LO3P265CU004.outbound.protection.outlook.com\n (mail-uksouthazon11020131.outbound.protection.outlook.com [52.101.196.131])\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 A7AF23DBD58;\n\tTue, 21 Apr 2026 14:56:42 +0000 (UTC)","from LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:488::16)\n by CWLP265MB5674.GBRP265.PROD.OUTLOOK.COM (2603:10a6:400:1b0::13) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9846.16; Tue, 21 Apr\n 2026 14:56:31 +0000","from LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM\n ([fe80::1c3:ceba:21b4:9986]) by LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM\n ([fe80::1c3:ceba:21b4:9986%4]) with mapi id 15.20.9846.016; Tue, 21 Apr 2026\n 14:56:31 +0000"],"ARC-Seal":["i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776783405; cv=fail;\n b=AFQQPlG+X1BoluCT1VtQkfH3ASXBDOcQLmzuUkaXVzTMVdsyPy2z3e/R65VTjWuyhFRyCEvLW9qJUIe7OU8ct3V9b4siTiXvsAVKek2z2wI7B7tg5HeH2FmhReegAqRA5idFQppHePPi/1oYxuXZh+h7Jvhuu3wybYr6Bz+kfm4=","i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=pC6AdZunnHueYMpO6vYS8KNiit9x953cnjUWSXli+hKC08eDq4C0PXD6qscv5uysJQEkDuUfSiS7wMjb3ig7St5kq/qQ/DW4+r8Oou7CtkX43lYvDYyAEKae3tp9i2k9Hb1xABJ6SyC1s1lZLnx2zoBF/PqftjzeN0r81s0uC9w5n85J5raZV2aMk3cPbiQC7dD/os3e6ot3/9axUIyECIk4jev/vhALwRzEJjg5M2r/j8YLCJKwAcaXmDUc579CJ36A9dZvR6fIBniSOg3ktEDdUyHtfsrJLCXFAUZu/KQI9G/xxcm/o0E/gl+MXHAsBifZswUPSaF9Mq4ejTgCHQ=="],"ARC-Message-Signature":["i=2; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776783405; c=relaxed/simple;\n\tbh=ERQyRg6FCVwG0U5GQXUSxvKmT2yPSztVRq39JXT1JUo=;\n\th=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To:\n\t To:Cc:MIME-Version;\n b=aKcMrrfvtRvKrZiSkvq65PLLQ58m9aMGNCeWlPDg63SamTom4xDNlH3LYo40h+Ud/uMkiYcUk4QvgFS5cZ/qsLdXJHl66GvejUwUK8SfIu7WLvhhHHLkf5zqv6t1CIJiTbc6ta+2q9nrgqC1Wl1BOU/ysytGC9RT21P6DOmNKng=","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=JhiXe8a3REvkFHSbZGLhsht++x7Hwq50WeRMOiDIXZ4=;\n b=m/F7H5twmpuQye+wa3EKHMxdxNP85SHgjLkbf8JF0K5LP10aZF7G0Qf4jIccc/0+dsI7Yx6Yy9G6TnGK5BAbftGL66/7VFOIe8+tfIBtZP8k9bjN1UtSsvpPFIXPTFgf8Mo2NjRtBbUaXHDJkJ4DTTlIQqWFfeLS8YvCnfAmPuH2S3sc8nwz+8Rcjk/K25xm7nlLXaqgx5a4gSbmijPtTmOTopT89xjHKBU+omGzcEmuONfK5qlKHQ0uLz62FxGqTin3jaDQGbi7/1ps3SWRKzN5hxJl/QTPKJ/pSQ6lSpNw8U2DBvNq4bmSjZVH0zlfP5pO6WBCbMoL5dY/5nu0yg=="],"ARC-Authentication-Results":["i=2; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=garyguo.net;\n spf=pass smtp.mailfrom=garyguo.net;\n dkim=pass (1024-bit key) header.d=garyguo.net header.i=@garyguo.net\n header.b=rd59Q9yf; arc=fail smtp.client-ip=52.101.196.131","i=1; mx.microsoft.com 1; spf=pass\n smtp.mailfrom=garyguo.net; dmarc=pass action=none header.from=garyguo.net;\n dkim=pass header.d=garyguo.net; arc=none"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=garyguo.net;\n s=selector1;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=JhiXe8a3REvkFHSbZGLhsht++x7Hwq50WeRMOiDIXZ4=;\n b=rd59Q9yfE8vZYQbqs4b8AVuzTNB4gbSwmNchd7seMo0PD8h4mMCF6vQnw+SFugOAuRMSEcCvjiHimVrVKUk9463co+XkMoFA1t31p2nt4NM+KWeKK5E3mzNUJBQ1IEBAW5DPgGHbW7T/440IR5i66IhSAB2N6EwGjZtWS+IDelI=","From":"Gary Guo <gary@garyguo.net>","Date":"Tue, 21 Apr 2026 15:56:22 +0100","Subject":"[PATCH v2 11/11] rust: io: add copying methods","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260421-io_projection-v2-11-4c251c692ef4@garyguo.net>","References":"<20260421-io_projection-v2-0-4c251c692ef4@garyguo.net>","In-Reply-To":"<20260421-io_projection-v2-0-4c251c692ef4@garyguo.net>","To":"Greg Kroah-Hartman <gregkh@linuxfoundation.org>,\n  \"Rafael J. Wysocki\" <rafael@kernel.org>, Danilo Krummrich <dakr@kernel.org>,\n  Miguel Ojeda <ojeda@kernel.org>, Boqun Feng <boqun@kernel.org>,\n  Gary Guo <gary@garyguo.net>,\n =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= <bjorn3_gh@protonmail.com>,\n  Benno Lossin <lossin@kernel.org>, Andreas Hindborg <a.hindborg@kernel.org>,\n  Alice Ryhl <aliceryhl@google.com>, Trevor Gross <tmgross@umich.edu>,\n  Daniel Almeida <daniel.almeida@collabora.com>,\n  Bjorn Helgaas <bhelgaas@google.com>,\n =?utf-8?q?Krzysztof_Wilczy=C5=84ski?= <kwilczynski@kernel.org>,\n  Abdiel Janulgue <abdiel.janulgue@gmail.com>,\n  Robin Murphy <robin.murphy@arm.com>,\n  Alexandre Courbot <acourbot@nvidia.com>, David Airlie <airlied@gmail.com>,\n  Simona Vetter <simona@ffwll.ch>","Cc":"driver-core@lists.linux.dev, rust-for-linux@vger.kernel.org,\n linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,\n nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org","X-Mailer":"b4 0.15.1","X-Developer-Signature":"v=1; a=ed25519-sha256; t=1776783386; l=11952;\n i=gary@garyguo.net; s=20221204; h=from:subject:message-id;\n bh=ERQyRg6FCVwG0U5GQXUSxvKmT2yPSztVRq39JXT1JUo=;\n b=uV59Tig1UAY86hboNAV4E3WIfI7Ql+9hlbKVgSxDejiR22aUNKPgwm2L01kkGD4YuxBthGXxB\n VK9/9YS3bVzBoD0RYoIqOm+9CZIpiMdSTwrbXneIfGvR8KxGbIJFG8S","X-Developer-Key":"i=gary@garyguo.net; a=ed25519;\n pk=vB3uIX95SM4eVrIqo1DWNWKDKD2xzB+yLLLr0yOPYMo=","X-ClientProxiedBy":"LO4P123CA0142.GBRP123.PROD.OUTLOOK.COM\n (2603:10a6:600:193::21) To LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM\n (2603:10a6:600:488::16)","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":"LOVP265MB8871:EE_|CWLP265MB5674:EE_","X-MS-Office365-Filtering-Correlation-Id":"85b87724-6a5a-469a-3a8f-08de9fb62c86","X-MS-Exchange-SenderADCheck":"1","X-MS-Exchange-AntiSpam-Relay":"0","X-Microsoft-Antispam":"\n\tBCL:0;ARA:13230040|1800799024|376014|7416014|10070799003|366016|921020|22082099003|18002099003|56012099003;","X-Microsoft-Antispam-Message-Info":"\n\torwlKEzGLNpwUFSx1iOk6ddBcck9W+HB7LIjcjLuWtfGqqO5Q7MN9w8ErEBMiQFEzbQMNLz35LfgfaYZ0hD4A9YrxjdPSCOfBFe9QsTI9PQhGT91T2b49Z82HrMQiZmL4i6h15hl1wOFrZNMhUaxTRYV8tsjhfHJXa1jRi+boIVrdcHh4lUQvwgxB+laWqxUsO3ceSmDpYl7NnJZx2hEBa4azfuIVqhrTsCxWw2IVIHYCHW5hU6CQY1RRNtD17iF12NMCrtJkvThkiJzUodaSVZzfSW2kMntRfkqIerqgNrCXqO+/g95l9gQlj0p4aBeT+I7y64CGFWoWSRif5JEo/yiUmzFLEB1XNIGyb3xsa9R0KZktAj01IRpOxWGTcyCTFmP7bvjTSnf+SXRjJtivvt40cvqW0ystH6SfTGEmHirS9jPt1BTmwG+sLeGS0/JZcRHdZWuTmh6q4fpRyTQrrOzgj/kuX5VTvb16mt3yg88HdZ4fmVBYvVB0zW4jD1wMVhwhBgrPj8j26joS+O6vt6IkhRtwVp+yNG153fHV1pb2RSU9KFLQlRss4MdNIFiK5BDpfGrHTtbZrNgOetI8VjUX7hrzW6rEw8b2ifVrjUlMWM94TWeDyOGLQRWzU17uxX8Z8KkzIBBA9aOhymiEDIyYlIfefufa2ZiQl6qW3prPsx3jwhB4cFxLlueD675eZLhWGJPIajTNH1n4rlKgebKzV3UV/lAN1MSPHx/jQtYfVkfJIjf80+qS/gPM+nLySDmnS4vPTZHTfohhMr+8g==","X-Forefront-Antispam-Report":"\n\tCIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(376014)(7416014)(10070799003)(366016)(921020)(22082099003)(18002099003)(56012099003);DIR:OUT;SFP:1102;","X-MS-Exchange-AntiSpam-MessageData-ChunkCount":"1","X-MS-Exchange-AntiSpam-MessageData-0":"=?utf-8?q?/l3KdRM1lHD+FLfYPyjro/r7CAxz?=\n\t=?utf-8?q?h9krEd9x6yxEXRRzVmH8KGtb2L2op3ChgT6fvw/Ycf3kVpPX5sGcB+q1a6y6fN5IJ?=\n\t=?utf-8?q?0ZLTMN9nUnPJ0aKkObWG78OKuYnaKE9lmjkVuhqpS7L3f704Tn7nlCzVcvOBqA0eM?=\n\t=?utf-8?q?8yQOkR6WsmXECmrqHJrlYkt13zxxeFRxzqC6ttQkbsm33MxW4OpFidCYaFbVrNZlc?=\n\t=?utf-8?q?HjnLByDU/82a4fdxcTRiVt044FuJ5w30d6UDHbn7+Q4gOQRCXLvkWMWFxi3TZkzEQ?=\n\t=?utf-8?q?jTuyK+Zghqf5EvgR4wSzabFA/pvqSQCxmQGS7/frAbWsWX9Zl6sz7AfMLLeDvLkYG?=\n\t=?utf-8?q?GYg1OY7JoX7yoa/mKN0RTjN7wRHrbFmYVjvs+LdQVktNhiqSbYp3VOy6SE+F949KU?=\n\t=?utf-8?q?AkVQ71veZ7RsQXgKwqPIPJcgczc7TOOLYno53RNX72pNYkHdiMcN7qZqJwF+JP0gR?=\n\t=?utf-8?q?O+q658SF1p0ITd9D3mzU7ZS69HKSD1IBFiZ7JIo6MxnEc5EJ66JiO8g7Cxux702Gt?=\n\t=?utf-8?q?kq/0xDrka1tlFcmkHMxUL3ah+TZKSbwpI1ptpGZuywtfgn/aFJ4AYksZHSLyY6/80?=\n\t=?utf-8?q?XWCRCMvk4t63th/B6DFYxaekB3lDaB9jKD3RRbRI4zHMcfHUGI1prgKSraERA5cwp?=\n\t=?utf-8?q?9EgEekHmBEgLpxqMcE3HAn9bcCby2pb5827rjHsGTUw8ja7+6KpYXzc7GFKJEYWUh?=\n\t=?utf-8?q?F3FDUfa9rd3UL3CQq0S54pzTM7mqM9Kl3Rhxou0bdCk5jrmwOlCag1fcVmyIC9xZn?=\n\t=?utf-8?q?78vvsmnAwTkWc4JWFKccDgVoyraADuCrq3w1KogP9out24ZRNp0oaT5DY9wTXL/WW?=\n\t=?utf-8?q?YxhfkVbi1Mk+RTt7+uGD67DoLHzsWGiIAizej/o0feTlJpSDg0UmuB2iEEFmSrlBM?=\n\t=?utf-8?q?wuBq+l46tAScTy9F3bKUliBZVmEqe2/OWw+e83WTf7zmSeGnA6lkngaFsLpbrAjYr?=\n\t=?utf-8?q?5L2GMRtmLAvTZg41r3iiN19guaj4ZUGSu03Z4vqnzZJHPv36c5fuunPot2dMIcSYs?=\n\t=?utf-8?q?yNaJHUj6Fu8qMV1Aqx3tr4IV31z0al1BRMZ5BlTZ3B4dcR1a6TF/BBjnOLxNTxYAt?=\n\t=?utf-8?q?sSO4CxpxXdxM3HX/z3PCrVP77N8NSOUq5Wty6jwFHai86+nQced/a4SX94f6+qh+5?=\n\t=?utf-8?q?uZF+9G8PKLIIGkRA05YtW+GElgYpk718cT80cIHdFaYf8qoX0W1IHRyzd1LJHnZqD?=\n\t=?utf-8?q?NNpPbG6DRG2GTRYDyPDuC54jzAK5jExx9ECGVMfyqSlSs2MDoUoSDMVo+Qrue+wwQ?=\n\t=?utf-8?q?B+o5zDBgAj9xh+XqM3xusl4tvh1Rl2KzLy4mO1qhSPt40utBcjCM+nrhWlEKF7h0t?=\n\t=?utf-8?q?DSd9HWORkc9GV3ep9zsle1/KR+w0IBZJcaDr1JUp96Li9Zo/4PQ650wujAgQ0kOyu?=\n\t=?utf-8?q?BTblnc/9PwaCGdgP9Qz+3wz/fa0ax386Le3MZ8CFr1zYl16ErfyVgY8ARk5dldi22?=\n\t=?utf-8?q?DzGomD7+KYyFI3T/vi4T12EIjAnx55uCaRPKO+hauuIhJ+ak9kUEzeHOhdljbMOyV?=\n\t=?utf-8?q?YLjyK5d22IoITwRJqCEXpmbFzlwxKefd0hbQgwyU2mKQVPDNHBTl9TpzXLW4yckwF?=\n\t=?utf-8?q?RCxOYUObAjodIfGOWgqmEV8lGgfVWL67yY+4PZ/dD+hHlkcQTICAmpUmXTAuqWJ0b?=\n\t=?utf-8?q?jHqDPcjuvaQwuz6Dh3XQlNx0qCmucZrA=3D=3D?=","X-OriginatorOrg":"garyguo.net","X-MS-Exchange-CrossTenant-Network-Message-Id":"\n 85b87724-6a5a-469a-3a8f-08de9fb62c86","X-MS-Exchange-CrossTenant-AuthSource":"LOVP265MB8871.GBRP265.PROD.OUTLOOK.COM","X-MS-Exchange-CrossTenant-AuthAs":"Internal","X-MS-Exchange-CrossTenant-OriginalArrivalTime":"21 Apr 2026 14:56:31.3720\n (UTC)","X-MS-Exchange-CrossTenant-FromEntityHeader":"Hosted","X-MS-Exchange-CrossTenant-Id":"bbc898ad-b10f-4e10-8552-d9377b823d45","X-MS-Exchange-CrossTenant-MailboxType":"HOSTED","X-MS-Exchange-CrossTenant-UserPrincipalName":"\n 3LV5lvyij58RXqf6uE0dooijMGQBJkU4DWKi7/H4dSu/JMv9ySwnImpIZCXowG/b9jW5oLzyb5xOpT7WvmVjJg==","X-MS-Exchange-Transport-CrossTenantHeadersStamped":"CWLP265MB5674"},"content":"One feature that was lost from the old `dma_read!()` and `dma_write!()`\nwhen moving to `io_read!()` and `io_write!()` was the ability to read/write\na large structs. However, the semantics was unclear to begin with, as there\nwas no guarantee about their atomicity even for structs that were small\nenough to fit in u32. Re-introduces the capability in the form of copying\nmethods.\n\n    dma_read!(foo, bar) -> io_project!(foo, bar).copy_read()\n    dma_write!(foo, bar, baz) -> io_project!(foo, bar).copy_write(baz)\n\nThe semantics for these are modelled after memcpy so user has clear\nexpectation of lack of atomicity. As an additional benefit of this change,\nthis now works for MMIO as well, which maps to `memcpy_{from,to}io`.\n\nFor slices, which is unsized so the API above can't work, `copy_from_slice`\nand `copy_to_slice` were added to copy from/to normal memory, and\n`copy_from_io_slice` and `copy_to_io_slice` were added to copy from/to\nother `Io` regions. They're optimized if at least one end is mapped to\nsystem memory; if none are, the copy occurs with an intermediate stack\nbuffer.\n\nSigned-off-by: Gary Guo <gary@garyguo.net>\n---\n rust/kernel/dma.rs |   8 +-\n rust/kernel/io.rs  | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 238 insertions(+), 1 deletion(-)","diff":"diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs\nindex bbdeb117c145..307f5769ca0a 100644\n--- a/rust/kernel/dma.rs\n+++ b/rust/kernel/dma.rs\n@@ -16,7 +16,8 @@\n     fs::file,\n     io::{\n         Io,\n-        IoCapable, //\n+        IoCapable,\n+        IoCopyable, //\n     },\n     prelude::*,\n     ptr::KnownSize,\n@@ -997,6 +998,11 @@ unsafe fn io_write(&self, value: $ty, address: *mut $ty) {\n     u64\n );\n \n+// SAFETY: `Coherent` is mapped to CPU address space.\n+unsafe impl<T: ?Sized + KnownSize> IoCopyable for Coherent<T> {\n+    const IS_MAPPED: bool = true;\n+}\n+\n impl<'a, B: ?Sized + KnownSize, T: ?Sized> crate::io::View<'a, Coherent<B>, T> {\n     /// Returns a DMA handle which may be given to the device as the DMA address base of\n     /// the region.\ndiff --git a/rust/kernel/io.rs b/rust/kernel/io.rs\nindex efcd7e6741d7..0b1ed68c0f9b 100644\n--- a/rust/kernel/io.rs\n+++ b/rust/kernel/io.rs\n@@ -4,6 +4,8 @@\n //!\n //! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)\n \n+use core::mem::MaybeUninit;\n+\n use crate::{\n     bindings,\n     prelude::*,\n@@ -233,6 +235,55 @@ pub trait IoCapable<T> {\n     unsafe fn io_write(&self, value: T, address: *mut T);\n }\n \n+/// Trait indicating that an I/O backend supports memory copy operations.\n+///\n+/// # Safety\n+///\n+/// If [`IS_MAPPED`] is overridden to true, it must be correct per documentation.\n+pub unsafe trait IoCopyable {\n+    /// Whether the pointers for this I/O backend are in the CPU address space, and are coherently\n+    /// mapped.\n+    ///\n+    /// When this is true, it means that memory can be accessed with byte-wise atomic memory copy.\n+    const IS_MAPPED: bool = false;\n+\n+    /// Copy `size` bytes from `address` to `buffer`.\n+    ///\n+    /// # Safety\n+    ///\n+    /// - The range `[address..address + size]` must be within the bounds of `Self`.\n+    /// - `buffer` is valid for write for `size` bytes.\n+    #[inline]\n+    unsafe fn copy_from_io(&self, address: *mut u8, buffer: *mut u8, size: usize) {\n+        const_assert!(Self::IS_MAPPED);\n+\n+        // Use `bindings::memcpy` instead of copy_nonoverlapping for volatile.\n+        // SAFETY:\n+        // - `buffer` is valid for write for `size` bytes.\n+        // - `IS_MAPPED` guarantees `address` is in CPU address space, with safety requirements\n+        //   `address` is valid for read for `size` bytes.\n+        unsafe { bindings::memcpy(buffer.cast(), address.cast(), size) };\n+    }\n+\n+    /// Copy `size` bytes from `buffer` to `address`.\n+    ///\n+    /// # Safety\n+    ///\n+    /// - The range `[address..address + size]` must be within the bounds of `Self`.\n+    /// - `buffer` is valid for read for `size` bytes.\n+    #[inline]\n+    unsafe fn copy_to_io(&self, address: *mut u8, buffer: *const u8, size: usize) {\n+        const_assert!(Self::IS_MAPPED);\n+\n+        // Use `bindings::memcpy` instead of copy_nonoverlapping for volatile.\n+        // SAFETY:\n+        // - `IS_MAPPED` guarantees `address` is in CPU address space, with safety requirements\n+        //   `address` is valid for write for `size` bytes.\n+        // - `buffer` is valid for read for `size` bytes.\n+        unsafe { bindings::memcpy(address.cast(), buffer.cast(), size) };\n+    }\n+}\n+\n /// Describes a given I/O location: its offset, width, and type to convert the raw value from and\n /// into.\n ///\n@@ -841,6 +892,19 @@ unsafe fn io_write(&self, value: $ty, address: *mut $ty) {\n     writeq\n );\n \n+// SAFETY: `IS_MAPPED` is not overridden.\n+unsafe impl<T: ?Sized + KnownSize> IoCopyable for Mmio<T> {\n+    unsafe fn copy_from_io(&self, address: *mut u8, buffer: *mut u8, size: usize) {\n+        // SAFETY: Per safety requirement.\n+        unsafe { bindings::memcpy_fromio(buffer.cast(), address.cast(), size) };\n+    }\n+\n+    unsafe fn copy_to_io(&self, address: *mut u8, buffer: *const u8, size: usize) {\n+        // SAFETY: Per safety requirement.\n+        unsafe { bindings::memcpy_toio(address.cast(), buffer.cast(), size) };\n+    }\n+}\n+\n impl<T: ?Sized + KnownSize> Io for Mmio<T> {\n     type Type = T;\n \n@@ -1029,6 +1093,173 @@ pub fn write_val(&self, value: T) {\n     }\n }\n \n+impl<'a, IO: ?Sized, T> View<'a, IO, [T]> {\n+    /// Returns the length of the slice in number of elements.\n+    #[inline]\n+    pub fn len(self) -> usize {\n+        self.as_ptr().len()\n+    }\n+\n+    /// Returns `true` if the slice has a length of 0.\n+    #[inline]\n+    pub fn is_empty(self) -> bool {\n+        self.len() == 0\n+    }\n+}\n+\n+impl<T, IO: ?Sized + Io + IoCopyable> View<'_, IO, T> {\n+    /// Copy-read from I/O memory.\n+    ///\n+    /// There is no atomicity gurantee.\n+    #[inline]\n+    pub fn copy_read(self) -> T\n+    where\n+        T: FromBytes,\n+    {\n+        // Optimized path if I/O backend is CPU mapped.\n+        if IO::IS_MAPPED {\n+            // SAFETY:\n+            // - `IS_MAPPED` guarantees `self.ptr` is in CPU address space, with type invariants\n+            //   `self.ptr` is valid for read for `size` bytes.\n+            // - `T: FromBytes` guarantee that all bit patterns are valid.\n+            // - Using read_volatile() here so that race with hardware is well-defined.\n+            // - Using read_volatile() here is not sound if it races with other CPU per Rust\n+            //   rules, but this is allowed per LKMM.\n+            return unsafe { self.ptr.read_volatile() };\n+        }\n+\n+        let mut buf = MaybeUninit::<T>::uninit();\n+        // SAFETY:\n+        // - Per type invariants.\n+        // - `buf.as_mut_ptr()` is valid for write for `size_of::<T>()` bytes.\n+        unsafe {\n+            self.io\n+                .copy_from_io(self.ptr.cast(), buf.as_mut_ptr().cast(), size_of::<T>())\n+        };\n+        // SAFETY: T: FromBytes` guarantee that all bit patterns are valid.\n+        unsafe { buf.assume_init() }\n+    }\n+\n+    /// Write to I/O memory.\n+    ///\n+    /// There is no atomicity gurantee.\n+    #[inline]\n+    pub fn copy_write(self, value: T)\n+    where\n+        T: AsBytes,\n+    {\n+        // Optimized path if I/O backend is CPU mapped.\n+        if IO::IS_MAPPED {\n+            // SAFETY:\n+            // - `IS_MAPPED` guarantees `self.ptr` is in CPU address space, with safety requirements\n+            //   `self.ptr` is valid for write for `size` bytes.\n+            // - Using write_volatile() here so that race with hardware is well-defined.\n+            // - Using write_volatile() here is not sound if it races with other CPU per Rust\n+            //   rules, but this is allowed per LKMM.\n+            unsafe { self.ptr.write_volatile(value) };\n+            return;\n+        }\n+\n+        // SAFETY:\n+        // - Per type invariants.\n+        // - `&raw const value` is valid for read for `size_of::<T>()` bytes.\n+        unsafe {\n+            self.io\n+                .copy_to_io(self.ptr.cast(), (&raw const value).cast(), size_of::<T>())\n+        };\n+        core::mem::forget(value);\n+    }\n+}\n+\n+impl<IO: ?Sized + Io + IoCopyable> View<'_, IO, [u8]> {\n+    /// Copy bytes from slice to I/O memory.\n+    #[inline]\n+    pub fn copy_from_slice(self, data: &[u8]) {\n+        assert_eq!(self.len(), data.len());\n+\n+        // SAFETY:\n+        // - Per type invariants.\n+        // - `data.as_ptr()` is valid for read for `data.len()` bytes.\n+        unsafe {\n+            self.io\n+                .copy_to_io(self.ptr.cast(), data.as_ptr(), data.len())\n+        }\n+    }\n+\n+    /// Copy bytes from I/O memory to slice.\n+    #[inline]\n+    pub fn copy_to_slice(self, data: &mut [u8]) {\n+        assert_eq!(self.len(), data.len());\n+\n+        // SAFETY:\n+        // - Per type invariants.\n+        // - `data.as_mut_ptr()` is valid for write for `data.len()` bytes.\n+        unsafe {\n+            self.io\n+                .copy_from_io(self.ptr.cast(), data.as_mut_ptr(), data.len())\n+        }\n+    }\n+\n+    fn copy_from_io_slice_via_buffer<T: ?Sized + Io + IoCopyable>(&self, data: View<'_, T, [u8]>) {\n+        let mut buf = MaybeUninit::<[u8; 256]>::uninit();\n+\n+        let mut dst_ptr = self.ptr.cast::<u8>();\n+        let mut src_ptr = data.ptr.cast::<u8>();\n+        let mut len = self.len();\n+\n+        while len != 0 {\n+            let copy_len = core::cmp::min(len, 256);\n+            // SAFETY:\n+            // - `src_ptr` is valid for I/O read of `copy_len` bytes per type invariants of `data`.\n+            // - `buf.as_mut_ptr()` is valid for write for `copy_len` bytes as `copy_len <= 256`.\n+            unsafe {\n+                data.io\n+                    .copy_from_io(src_ptr, buf.as_mut_ptr().cast(), copy_len)\n+            };\n+\n+            // SAFETY:\n+            // - `dst_ptr` is valid for I/O write of `copy_len` bytes per type invariants of `self`.\n+            // - `buf.as_mut_ptr()` is valid for write for `copy_len` bytes as `copy_len <= 256`.\n+            unsafe { self.io.copy_to_io(dst_ptr, buf.as_ptr().cast(), copy_len) };\n+\n+            dst_ptr = dst_ptr.wrapping_add(copy_len);\n+            src_ptr = src_ptr.wrapping_add(copy_len);\n+            len -= copy_len;\n+        }\n+    }\n+\n+    /// Copy bytes from `data` I/O slice to the `self` I/O slice.\n+    pub fn copy_from_io_slice<T: ?Sized + Io + IoCopyable>(&self, data: View<'_, T, [u8]>) {\n+        assert_eq!(self.len(), data.len());\n+\n+        if T::IS_MAPPED {\n+            // SAFETY:\n+            // - Per type invariants.\n+            // - `data.as_ptr()` is valid for read for `data.len()` bytes.\n+            unsafe {\n+                self.io\n+                    .copy_to_io(self.ptr.cast(), data.as_ptr().cast(), data.len())\n+            }\n+        } else if IO::IS_MAPPED {\n+            // SAFETY:\n+            // - Per type invariants.\n+            // - `self.as_ptr()` is valid for write for `self.len()` bytes.\n+            unsafe {\n+                data.io\n+                    .copy_from_io(data.ptr.cast(), self.as_ptr().cast(), self.len())\n+            }\n+        } else {\n+            self.copy_from_io_slice_via_buffer(data)\n+        }\n+    }\n+\n+    /// Copy bytes from `self` I/O slice to the `data` I/O slice.\n+    #[inline]\n+    pub fn copy_to_io_slice<T: ?Sized + Io + IoCopyable>(self, data: View<'_, T, [u8]>) {\n+        data.copy_from_io_slice(self)\n+    }\n+}\n+\n /// Project an I/O type to a subview of it.\n ///\n /// The syntax is of form `io_project!(io, proj)` where `io` is an expression to a type that\n","prefixes":["v2","11/11"]}