Skip to content

Commit 2580da3

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 e51732a commit 2580da3

2 files changed

Lines changed: 89 additions & 34 deletions

File tree

drivers/pci/p2pdma.c

Lines changed: 84 additions & 34 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,82 @@ 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;
254+
255+
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
256+
if (p2p)
257+
/* PCI device was "rebound" to the driver */
258+
return &p2p->mem;
241259

242260
p2p = devm_kzalloc(&pdev->dev, sizeof(*p2p), GFP_KERNEL);
243261
if (!p2p)
244-
return -ENOMEM;
262+
return ERR_PTR(-ENOMEM);
245263

246264
xa_init(&p2p->map_types);
265+
p2p->mem.owner = &pdev->dev;
266+
/* On all p2p platforms bus_offset is the same for all BARs */
267+
p2p->mem.bus_offset =
268+
pci_bus_address(pdev, 0) - pci_resource_start(pdev, 0);
247269

248-
p2p->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(&pdev->dev));
249-
if (!p2p->pool)
250-
goto out;
270+
ret = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_release, pdev);
271+
if (ret)
272+
goto out_p2p;
251273

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

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

260-
rcu_assign_pointer(pdev->p2pdma, p2p);
261301
return 0;
262302

263303
out_pool_destroy:
264-
gen_pool_destroy(p2p->pool);
265-
out:
266-
devm_kfree(&pdev->dev, p2p);
267-
return error;
304+
gen_pool_destroy(p2pdma->pool);
305+
p2pdma->pool = NULL;
306+
return ret;
268307
}
269308

270309
static void pci_p2pdma_unmap_mappings(void *data)
@@ -276,7 +315,7 @@ static void pci_p2pdma_unmap_mappings(void *data)
276315
* unmap_mapping_range() on the inode, teardown any existing userspace
277316
* mappings and prevent new ones from being created.
278317
*/
279-
sysfs_remove_file_from_group(&p2p_pgmap->mem.owner->kobj,
318+
sysfs_remove_file_from_group(&p2p_pgmap->mem->owner->kobj,
280319
&p2pmem_alloc_attr.attr,
281320
p2pmem_group.name);
282321
}
@@ -295,6 +334,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
295334
u64 offset)
296335
{
297336
struct pci_p2pdma_pagemap *p2p_pgmap;
337+
struct p2pdma_provider *mem;
298338
struct dev_pagemap *pgmap;
299339
struct pci_p2pdma *p2pdma;
300340
void *addr;
@@ -312,25 +352,33 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
312352
if (size + offset > pci_resource_len(pdev, bar))
313353
return -EINVAL;
314354

315-
if (!pdev->p2pdma) {
316-
error = pci_p2pdma_setup(pdev);
355+
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
356+
if (!p2pdma) {
357+
mem = pci_p2pdma_enable(pdev);
358+
if (IS_ERR(mem))
359+
return PTR_ERR(mem);
360+
361+
error = pci_p2pdma_setup_pool(pdev);
317362
if (error)
318363
return error;
319-
}
364+
365+
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
366+
} else
367+
mem = &p2pdma->mem;
320368

321369
p2p_pgmap = devm_kzalloc(&pdev->dev, sizeof(*p2p_pgmap), GFP_KERNEL);
322-
if (!p2p_pgmap)
323-
return -ENOMEM;
370+
if (!p2p_pgmap) {
371+
error = -ENOMEM;
372+
goto free_pool;
373+
}
324374

325375
pgmap = &p2p_pgmap->pgmap;
326376
pgmap->range.start = pci_resource_start(pdev, bar) + offset;
327377
pgmap->range.end = pgmap->range.start + size - 1;
328378
pgmap->nr_range = 1;
329379
pgmap->type = MEMORY_DEVICE_PCI_P2PDMA;
330380
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);
381+
p2p_pgmap->mem = mem;
334382

335383
addr = devm_memremap_pages(&pdev->dev, pgmap);
336384
if (IS_ERR(addr)) {
@@ -343,7 +391,6 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
343391
if (error)
344392
goto pages_free;
345393

346-
p2pdma = rcu_dereference_protected(pdev->p2pdma, 1);
347394
error = gen_pool_add_owner(p2pdma->pool, (unsigned long)addr,
348395
pci_bus_address(pdev, bar) + offset,
349396
range_len(&pgmap->range), dev_to_node(&pdev->dev),
@@ -359,7 +406,10 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
359406
pages_free:
360407
devm_memunmap_pages(&pdev->dev, pgmap);
361408
pgmap_free:
362-
devm_kfree(&pdev->dev, pgmap);
409+
devm_kfree(&pdev->dev, p2p_pgmap);
410+
free_pool:
411+
sysfs_remove_group(&pdev->dev.kobj, &p2pmem_group);
412+
gen_pool_destroy(p2pdma->pool);
363413
return error;
364414
}
365415
EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource);
@@ -1008,11 +1058,11 @@ void __pci_p2pdma_update_state(struct pci_p2pdma_map_state *state,
10081058
{
10091059
struct pci_p2pdma_pagemap *p2p_pgmap = to_p2p_pgmap(page_pgmap(page));
10101060

1011-
if (state->mem == &p2p_pgmap->mem)
1061+
if (state->mem == p2p_pgmap->mem)
10121062
return;
10131063

1014-
state->mem = &p2p_pgmap->mem;
1015-
state->map = pci_p2pdma_map_type(&p2p_pgmap->mem, dev);
1064+
state->mem = p2p_pgmap->mem;
1065+
state->map = pci_p2pdma_map_type(p2p_pgmap->mem, dev);
10161066
}
10171067

10181068
/**

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)