Skip to content

Commit 25d4ce2

Browse files
jgowansfllinden
authored andcommitted
Introduce page touching DMA ops binding
Allows enabling page touching via a kernel command line parameter. When enabled, devices which don't have an IOMMU assigned to them will be assigned the page touching DMA map ops which ensures that any memory mapped for DMA by that devices will be accessed by the CPU to make it resident. Signed-off-by: James Gowans <[email protected]> Cc-Team: kaos-brimstone <[email protected]> Cc-Team: ec2-memo <[email protected]>
1 parent f4bf875 commit 25d4ce2

File tree

6 files changed

+197
-0
lines changed

6 files changed

+197
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13191,6 +13191,13 @@ F: include/net/page_pool.h
1319113191
F: include/trace/events/page_pool.h
1319213192
F: net/core/page_pool.c
1319313193

13194+
PAGE TOUCHING DMA
13195+
M: James Gowans <[email protected]>
13196+
13197+
S: Supported
13198+
F: include/linux/dma-page-touching.h
13199+
F: kernel/dma/page_touching.c
13200+
1319413201
PANASONIC LAPTOP ACPI EXTRAS DRIVER
1319513202
M: Harald Welte <[email protected]>
1319613203

arch/arm64/mm/dma-mapping.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/cache.h>
99
#include <linux/dma-map-ops.h>
1010
#include <linux/dma-iommu.h>
11+
#include <linux/dma-page-touching.h>
1112
#include <xen/xen.h>
1213
#include <xen/swiotlb-xen.h>
1314

@@ -56,4 +57,9 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
5657
if (xen_initial_domain())
5758
dev->dma_ops = &xen_swiotlb_dma_ops;
5859
#endif
60+
61+
#ifdef CONFIG_DMA_PAGE_TOUCHING
62+
if (!dev->dma_ops)
63+
setup_dma_page_touching_ops(dev);
64+
#endif
5965
}

include/linux/dma-page-touching.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
2+
/*
3+
* Copyright 2021 Amazon.com, Inc. or its affiliates.
4+
*
5+
* This software is available to you under a choice of one of two
6+
* licenses. You may choose to be licensed under the terms of the GNU
7+
* General Public License (GPL) Version 2, available from the file
8+
* COPYING in the main directory of this source tree, or the
9+
* BSD license below:
10+
*
11+
* Redistribution and use in source and binary forms, with or
12+
* without modification, are permitted provided that the following
13+
* conditions are met:
14+
*
15+
* - Redistributions of source code must retain the above
16+
* copyright notice, this list of conditions and the following
17+
* disclaimer.
18+
*
19+
* - Redistributions in binary form must reproduce the above
20+
* copyright notice, this list of conditions and the following
21+
* disclaimer in the documentation and/or other materials
22+
* provided with the distribution.
23+
*
24+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31+
* SOFTWARE.
32+
*/
33+
34+
/*
35+
* Sets the supplied device's DMA ops to the page toucing DMA ops if
36+
* page touching is enabled and the device does not already have
37+
* DMA ops assigned.
38+
*/
39+
void setup_dma_page_touching_ops(struct device *dev);

kernel/dma/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ config DMA_OPS
2020
config DMA_OPS_BYPASS
2121
bool
2222

23+
config DMA_PAGE_TOUCHING
24+
bool "Support touching pages when allocated for DMA"
25+
help
26+
Builds in support for binding page touching DMA ops to devices which
27+
don't have an IOMMU. Memory mapped for DMA by those devices will be
28+
access by the CPU via the page touching dma_map_ops to ensure that
29+
the memory is resident when running on a memory overcommit host.
30+
The capacility must still be set up at boot time via the
31+
page_touching.dma_page_touching_enable kernel command line param.
32+
2333
config NEED_SG_DMA_LENGTH
2434
bool
2535

kernel/dma/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ obj-$(CONFIG_DMA_API_DEBUG) += debug.o
1010
obj-$(CONFIG_SWIOTLB) += swiotlb.o
1111
obj-$(CONFIG_DMA_COHERENT_POOL) += pool.o
1212
obj-$(CONFIG_DMA_REMAP) += remap.o
13+
obj-$(CONFIG_DMA_PAGE_TOUCHING) += page_touching.o

kernel/dma/page_touching.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
2+
/*
3+
* Copyright 2020 Amazon.com, Inc. or its affiliates.
4+
*
5+
* This software is available to you under a choice of one of two
6+
* licenses. You may choose to be licensed under the terms of the GNU
7+
* General Public License (GPL) Version 2, available from the file
8+
* COPYING in the main directory of this source tree, or the
9+
* BSD license below:
10+
*
11+
* Redistribution and use in source and binary forms, with or
12+
* without modification, are permitted provided that the following
13+
* conditions are met:
14+
*
15+
* - Redistributions of source code must retain the above
16+
* copyright notice, this list of conditions and the following
17+
* disclaimer.
18+
*
19+
* - Redistributions in binary form must reproduce the above
20+
* copyright notice, this list of conditions and the following
21+
* disclaimer in the documentation and/or other materials
22+
* provided with the distribution.
23+
*
24+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28+
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29+
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31+
* SOFTWARE.
32+
*/
33+
34+
#include <linux/dma-map-ops.h>
35+
#include "direct.h"
36+
#include <linux/moduleparam.h>
37+
38+
/*
39+
* A wrapper around dma_direct which does a readl on the memory being mapped
40+
* for DMA to ensure that it becomes resident.
41+
* Useful when running in a memory overcommit environment with lazy allocation
42+
* and free page reporting.
43+
*/
44+
45+
/*
46+
* Set with kernel cmd line param:
47+
* page_touching.dma_page_touching_enable=y
48+
*/
49+
static bool dma_page_touching_enable __ro_after_init;
50+
module_param_named(dma_page_touching_enable, dma_page_touching_enable, bool, 0400);
51+
MODULE_PARM_DESC(dma_page_touching_enable,
52+
"Touch pages allocated for DMA to ensure they are resident");
53+
54+
static void touch_each_page(void *start_addr, size_t size)
55+
{
56+
int addr_offset;
57+
58+
for (addr_offset = 0; addr_offset < size; addr_offset += PAGE_SIZE)
59+
__raw_readl((char *)start_addr + addr_offset);
60+
}
61+
62+
static void *page_touching_dma_alloc(struct device *dev, size_t size,
63+
dma_addr_t *dma_handle, gfp_t gfp,
64+
unsigned long attrs)
65+
{
66+
char *kaddr = dma_direct_alloc(dev, size, dma_handle, gfp, attrs);
67+
68+
if (!kaddr)
69+
return NULL;
70+
touch_each_page(kaddr, size);
71+
return kaddr;
72+
73+
}
74+
75+
static dma_addr_t page_touching_dma_map_page(struct device *dev, struct page *page,
76+
unsigned long offset, size_t size,
77+
enum dma_data_direction dir,
78+
unsigned long attrs)
79+
{
80+
dma_addr_t dma_handle = dma_direct_map_page(dev, page, offset, size, dir, attrs);
81+
82+
if (!(dma_mapping_error(dev, dma_handle)))
83+
touch_each_page(page_to_virt(page) + offset, size);
84+
return dma_handle;
85+
}
86+
87+
static int page_touching_dma_map_sg(struct device *dev, struct scatterlist *sglist,
88+
int nents, enum dma_data_direction dir,
89+
unsigned long attrs)
90+
{
91+
struct scatterlist *sg;
92+
int i, ret = dma_direct_map_sg(dev, sglist, nents, dir, attrs);
93+
94+
if (!ret)
95+
goto out;
96+
97+
for_each_sg(sglist, sg, nents, i)
98+
touch_each_page(page_to_virt(sg_page(sg)) + sg->offset, sg->length);
99+
100+
out:
101+
return ret;
102+
103+
}
104+
105+
/*
106+
* Only a portion of the dma_map_ops interface is implemented here; enough for
107+
* the EC2 ENA / NVMe drivers to work.
108+
* Notibly missing is alloc_pages.
109+
*/
110+
const static struct dma_map_ops page_touching_dma_ops = {
111+
.alloc = page_touching_dma_alloc,
112+
.free = dma_direct_free,
113+
.mmap = dma_common_mmap,
114+
.map_page = page_touching_dma_map_page,
115+
.unmap_page = dma_direct_unmap_page,
116+
.map_sg = page_touching_dma_map_sg,
117+
.unmap_sg = dma_direct_unmap_sg,
118+
.dma_supported = dma_direct_supported,
119+
.sync_single_for_cpu = dma_direct_sync_single_for_cpu,
120+
.sync_single_for_device = dma_direct_sync_single_for_device,
121+
.sync_sg_for_cpu = dma_direct_sync_sg_for_cpu,
122+
.dma_supported = dma_direct_supported,
123+
.get_required_mask = dma_direct_get_required_mask,
124+
.max_mapping_size = dma_direct_max_mapping_size,
125+
};
126+
127+
void setup_dma_page_touching_ops(struct device *dev)
128+
{
129+
if (!dma_page_touching_enable || dev->dma_ops)
130+
return;
131+
132+
dev_info(dev, "binding to page touching DMA ops\n");
133+
dev->dma_ops = &page_touching_dma_ops;
134+
}

0 commit comments

Comments
 (0)