Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

comedi: allocate DMA coherent buffer as individual pages

Depending on the driver, the acquisition buffer is allocated either from
normal memory, or from DMA coherent memory. For normal memory, the
buffer is allocated as individual pages, but for DMA coherent memory, it
is allocated as a single block. Prior to commit e36472145aa7 ("staging:
comedi: use dma_mmap_coherent for DMA-able buffer mmap"), the buffer was
allocated as individual pages for DMA coherent memory too, but that was
changed to allocate it as a single block to allow `dma_mmap_coherent()`
to be used to mmap it, because that requires the pages being mmap'ed to
be contiguous.

This patch allocates the buffer from DMA coherent memory a page at a
time again, and works around the limitation of `dma_mmap_coherent()` by
calling it in a loop for each page, with temporarily modified `vm_start`
and `vm_end` values in the VMA. (The `vm_pgoff` value is 0.)

Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Link: https://lore.kernel.org/r/20250415114008.5977-5-abbotti@mev.co.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Ian Abbott and committed by
Greg Kroah-Hartman
fd1575e2 5117f28a

+42 -44
+14 -29
drivers/comedi/comedi_buf.c
··· 27 27 28 28 if (bm->page_list) { 29 29 if (bm->dma_dir != DMA_NONE) { 30 - /* 31 - * DMA buffer was allocated as a single block. 32 - * Address is in page_list[0]. 33 - */ 34 - buf = &bm->page_list[0]; 35 - dma_free_coherent(bm->dma_hw_dev, 36 - PAGE_SIZE * bm->n_pages, 37 - buf->virt_addr, buf->dma_addr); 30 + for (i = 0; i < bm->n_pages; i++) { 31 + buf = &bm->page_list[i]; 32 + dma_free_coherent(bm->dma_hw_dev, PAGE_SIZE, 33 + buf->virt_addr, 34 + buf->dma_addr); 35 + } 38 36 } else { 39 37 for (i = 0; i < bm->n_pages; i++) { 40 38 buf = &bm->page_list[i]; ··· 86 88 goto err; 87 89 88 90 if (bm->dma_dir != DMA_NONE) { 89 - void *virt_addr; 90 - dma_addr_t dma_addr; 91 - 92 - /* 93 - * Currently, the DMA buffer needs to be allocated as a 94 - * single block so that it can be mmap()'ed. 95 - */ 96 - virt_addr = dma_alloc_coherent(bm->dma_hw_dev, 97 - PAGE_SIZE * n_pages, &dma_addr, 98 - GFP_KERNEL); 99 - if (!virt_addr) 100 - goto err; 101 - 102 91 for (i = 0; i < n_pages; i++) { 103 92 buf = &bm->page_list[i]; 104 - buf->virt_addr = virt_addr + (i << PAGE_SHIFT); 105 - buf->dma_addr = dma_addr + (i << PAGE_SHIFT); 93 + buf->virt_addr = 94 + dma_alloc_coherent(bm->dma_hw_dev, PAGE_SIZE, 95 + &buf->dma_addr, GFP_KERNEL); 96 + if (!buf->virt_addr) 97 + break; 106 98 } 107 - 108 - bm->n_pages = i; 109 99 } else { 110 100 for (i = 0; i < n_pages; i++) { 111 101 buf = &bm->page_list[i]; ··· 103 117 104 118 SetPageReserved(virt_to_page(buf->virt_addr)); 105 119 } 106 - 107 - bm->n_pages = i; 108 - if (i < n_pages) 109 - goto err; 110 120 } 121 + bm->n_pages = i; 122 + if (i < n_pages) 123 + goto err; 111 124 112 125 return bm; 113 126
+28 -15
drivers/comedi/comedi_fops.c
··· 2387 2387 goto done; 2388 2388 } 2389 2389 if (bm->dma_dir != DMA_NONE) { 2390 + unsigned long vm_start = vma->vm_start; 2391 + unsigned long vm_end = vma->vm_end; 2392 + 2390 2393 /* 2391 - * DMA buffer was allocated as a single block. 2392 - * Address is in page_list[0]. 2394 + * Buffer pages are not contiguous, so temporarily modify VMA 2395 + * start and end addresses for each buffer page. 2393 2396 */ 2394 - buf = &bm->page_list[0]; 2395 - retval = dma_mmap_coherent(bm->dma_hw_dev, vma, buf->virt_addr, 2396 - buf->dma_addr, n_pages * PAGE_SIZE); 2397 + for (i = 0; i < n_pages; ++i) { 2398 + buf = &bm->page_list[i]; 2399 + vma->vm_start = start; 2400 + vma->vm_end = start + PAGE_SIZE; 2401 + retval = dma_mmap_coherent(bm->dma_hw_dev, vma, 2402 + buf->virt_addr, 2403 + buf->dma_addr, PAGE_SIZE); 2404 + if (retval) 2405 + break; 2406 + 2407 + start += PAGE_SIZE; 2408 + } 2409 + vma->vm_start = vm_start; 2410 + vma->vm_end = vm_end; 2397 2411 } else { 2398 2412 for (i = 0; i < n_pages; ++i) { 2399 2413 unsigned long pfn; ··· 2421 2407 2422 2408 start += PAGE_SIZE; 2423 2409 } 2410 + } 2424 2411 2425 2412 #ifdef CONFIG_MMU 2426 - /* 2427 - * Leaving behind a partial mapping of a buffer we're about to 2428 - * drop is unsafe, see remap_pfn_range_notrack(). 2429 - * We need to zap the range here ourselves instead of relying 2430 - * on the automatic zapping in remap_pfn_range() because we call 2431 - * remap_pfn_range() in a loop. 2432 - */ 2433 - if (retval) 2434 - zap_vma_ptes(vma, vma->vm_start, size); 2413 + /* 2414 + * Leaving behind a partial mapping of a buffer we're about to drop is 2415 + * unsafe, see remap_pfn_range_notrack(). We need to zap the range 2416 + * here ourselves instead of relying on the automatic zapping in 2417 + * remap_pfn_range() because we call remap_pfn_range() in a loop. 2418 + */ 2419 + if (retval) 2420 + zap_vma_ptes(vma, vma->vm_start, size); 2435 2421 #endif 2436 - } 2437 2422 2438 2423 if (retval == 0) { 2439 2424 vma->vm_ops = &comedi_vm_ops;