Skip to content

Commit 4b3b0e0

Browse files
rleonkawasaki
authored andcommitted
PCI/P2PDMA: Refactor to separate core P2P functionality from memory allocation
Refactor the PCI P2PDMA subsystem to separate the core peer-to-peer DMA functionality from the optional memory allocation layer. This creates a two-tier architecture: The core layer provides P2P mapping functionality for physical addresses based on PCI device MMIO BARs and integrates with the DMA API for mapping operations. This layer is required for all P2PDMA users. The optional upper layer provides memory allocation capabilities including gen_pool allocator, struct page support, and sysfs interface for user space access. This separation allows subsystems like VFIO to use only the core P2P mapping functionality without the overhead of memory allocation features they don't need. The core functionality is now available through the new pci_p2pdma_enable() function that returns a p2pdma_provider structure. Signed-off-by: Leon Romanovsky <[email protected]>
1 parent 18ed580 commit 4b3b0e0

2 files changed

Lines changed: 80 additions & 33 deletions

File tree

drivers/pci/p2pdma.c

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ struct pci_p2pdma {
2525
struct gen_pool *pool;
2626
bool p2pmem_published;
2727
struct xarray map_types;
28+
struct p2pdma_provider mem;
2829
};
2930

3031
struct pci_p2pdma_pagemap {
3132
struct dev_pagemap pgmap;
32-
struct p2pdma_provider mem;
33+
struct p2pdma_provider *mem;
3334
};
3435

3536
static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap)
@@ -204,7 +205,7 @@ static void p2pdma_page_free(struct page *page)
204205
struct pci_p2pdma_pagemap *pgmap = to_p2p_pgmap(page_pgmap(page));
205206
/* safe to dereference while a reference is held to the percpu ref */
206207
struct pci_p2pdma *p2pdma = rcu_dereference_protected(
207-
to_pci_dev(pgmap->mem.owner)->p2pdma, 1);
208+
to_pci_dev(pgmap->mem->owner)->p2pdma, 1);
208209
struct percpu_ref *ref;
209210

210211
gen_pool_free_owner(p2pdma->pool, (uintptr_t)page_to_virt(page),
@@ -227,44 +228,77 @@ static void pci_p2pdma_release(void *data)
227228

228229
/* Flush and disable pci_alloc_p2p_mem() */
229230
pdev->p2pdma = NULL;
230-
synchronize_rcu();
231+
if (p2pdma->pool)
232+
synchronize_rcu();
233+
xa_destroy(&p2pdma->map_types);
234+
235+
if (!p2pdma->pool)
236+
return;
231237

232238
gen_pool_destroy(p2pdma->pool);
233239
sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
234-
xa_destroy(&p2pdma->map_types);
235240
}
236241

237-
static int pci_p2pdma_setup(struct pci_dev *pdev)
242+
/**
243+
* pci_p2pdma_enable - Enable peer-to-peer DMA support for a PCI device
244+
* @pdev: The PCI device to enable P2PDMA for
245+
*
246+
* This function initializes the peer-to-peer DMA infrastructure for a PCI
247+
* device. It allocates and sets up the necessary data structures to support
248+
* P2PDMA operations, including mapping type tracking.
249+
*/
250+
struct p2pdma_provider *pci_p2pdma_enable(struct pci_dev *pdev)
238251
{
239-
int error = -ENOMEM;
240252
struct pci_p2pdma *p2p;
253+
int ret;
241254

242255
p2p = devm_kzalloc(&pdev->dev, sizeof(*p2p), GFP_KERNEL);
243256
if (!p2p)
244-
return -ENOMEM;
257+
return ERR_PTR(-ENOMEM);
245258

246259
xa_init(&p2p->map_types);
260+
p2p->mem.owner = &pdev->dev;
261+
/* On all p2p platforms bus_offset is the same for all BARs */
262+
p2p->mem.bus_offset =
263+
pci_bus_address(pdev, 0) - pci_resource_start(pdev, 0);
247264

248-
p2p->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
249-
if (!p2p->pool)
250-
goto out;
265+
ret = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
266+
if (ret)
267+
goto out_p2p;
251268

252-
error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
253-
if (error)
254-
goto out_pool_destroy;
269+
rcu_assign_pointer(pdev->p2pdma, p2p);
270+
return &p2p->mem;
255271

256-
error = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
257-
if (error)
272+
out_p2p:
273+
devm_kfree(&pdev->dev, p2p);
274+
return ERR_PTR(ret);
275+
}
276+
EXPORT_SYMBOL_GPL(pci_p2pdma_enable);
277+
278+
static int pci_p2pdma_setup_pool(struct pci_dev *pdev)
279+
{
280+
struct pci_p2pdma *p2pdma;
281+
int ret;
282+
283+
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
284+
if (p2pdma->pool)
285+
/* We already setup pools, do nothing, */
286+
return 0;
287+
288+
p2pdma->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
289+
if (!p2pdma->pool)
290+
return -ENOMEM;
291+
292+
ret = sysfs_create_group(&pdev->dev.kobj, &p2pmem_group);
293+
if (ret)
258294
goto out_pool_destroy;
259295

260-
rcu_assign_pointer(pdev->p2pdma, p2p);
261296
return 0;
262297

263298
out_pool_destroy:
264-
gen_pool_destroy(p2p->pool);
265-
out:
266-
devm_kfree(&pdev->dev, p2p);
267-
return error;
299+
gen_pool_destroy(p2pdma->pool);
300+
p2pdma->pool = NULL;
301+
return ret;
268302
}
269303

270304
static void pci_p2pdma_unmap_mappings(void *data)
@@ -276,7 +310,7 @@ static void pci_p2pdma_unmap_mappings(void *data)
276310
* unmap_mapping_range() on the inode, teardown any existing userspace
277311
* mappings and prevent new ones from being created.
278312
*/
279-
sysfs_remove_file_from_group(&p2p_pgmap->mem.owner->kobj,
313+
sysfs_remove_file_from_group(&p2p_pgmap->mem->owner->kobj,
280314
&p2pmem_alloc_attr.attr,
281315
p2pmem_group.name);
282316
}
@@ -295,6 +329,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
295329
u64 offset)
296330
{
297331
struct pci_p2pdma_pagemap *p2p_pgmap;
332+
struct p2pdma_provider *mem;
298333
struct dev_pagemap *pgmap;
299334
struct pci_p2pdma *p2pdma;
300335
void *addr;
@@ -312,25 +347,30 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
312347
if (size + offset > pci_resource_len(pdev, bar))
313348
return -EINVAL;
314349

315-
if (!pdev->p2pdma) {
316-
error = pci_p2pdma_setup(pdev);
350+
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
351+
if (!p2pdma) {
352+
mem = pci_p2pdma_enable(pdev);
353+
if (IS_ERR(mem))
354+
return PTR_ERR(mem);
355+
356+
error = pci_p2pdma_setup_pool(pdev);
317357
if (error)
318358
return error;
319359
}
320360

321361
p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL);
322-
if (!p2p_pgmap)
323-
return -ENOMEM;
362+
if (!p2p_pgmap) {
363+
error = -ENOMEM;
364+
goto free_pool;
365+
}
324366

325367
pgmap = &p2p_pgmap->pgmap;
326368
pgmap->range.start = pci_resource_start(pdev, bar) + offset;
327369
pgmap->range.end = pgmap->range.start + size - 1;
328370
pgmap->nr_range = 1;
329371
pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
330372
pgmap->ops = &p2pdma_pgmap_ops;
331-
p2p_pgmap->mem.owner = &pdev->dev;
332-
p2p_pgmap->mem.bus_offset =
333-
pci_bus_address(pdev, bar) - pci_resource_start(pdev, bar);
373+
p2p_pgmap->mem = mem;
334374

335375
addr = devm_memremap_pages(&pdev->dev, pgmap);
336376
if (IS_ERR(addr)) {
@@ -343,7 +383,6 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
343383
if (error)
344384
goto pages_free;
345385

346-
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
347386
error = gen_pool_add_owner(p2pdma->pool, (unsigned long)addr,
348387
pci_bus_address(pdev, bar) + offset,
349388
range_len(&pgmap->range), dev_to_node(&pdev->dev),
@@ -359,7 +398,10 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
359398
pages_free:
360399
devm_memunmap_pages(&pdev->dev, pgmap);
361400
pgmap_free:
362-
devm_kfree(&pdev->dev, pgmap);
401+
devm_kfree(&pdev->dev, p2p_pgmap);
402+
free_pool:
403+
sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
404+
gen_pool_destroy(p2pdma->pool);
363405
return error;
364406
}
365407
EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource);
@@ -1008,11 +1050,11 @@ void __pci_p2pdma_update_state(struct pci_p2pdma_map_state *state,
10081050
{
10091051
struct pci_p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(page_pgmap(page));
10101052

1011-
if (state->mem == &p2p_pgmap->mem)
1053+
if (state->mem == p2p_pgmap->mem)
10121054
return;
10131055

1014-
state->mem = &p2p_pgmap->mem;
1015-
state->map = pci_p2pdma_map_type(&p2p_pgmap->mem, dev);
1056+
state->mem = p2p_pgmap->mem;
1057+
state->map = pci_p2pdma_map_type(p2p_pgmap->mem, dev);
10161058
}
10171059

10181060
/**

include/linux/pci-p2pdma.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct p2pdma_provider {
2727
};
2828

2929
#ifdef CONFIG_PCI_P2PDMA
30+
struct p2pdma_provider *pci_p2pdma_enable(struct pci_dev *pdev);
3031
int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
3132
u64 offset);
3233
int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients,
@@ -45,6 +46,10 @@ int pci_p2pdma_enable_store(const char *page, struct pci_dev **p2p_dev,
4546
ssize_t pci_p2pdma_enable_show(char *page, struct pci_dev *p2p_dev,
4647
bool use_p2pdma);
4748
#else /* CONFIG_PCI_P2PDMA */
49+
static inline struct p2pdma_provider *pci_p2pdma_enable(struct pci_dev *pdev)
50+
{
51+
return ERR_PTR(-EOPNOTSUPP);
52+
}
4853
static inline int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar,
4954
size_t size, u64 offset)
5055
{

0 commit comments

Comments
 (0)