It is desirable to perform "zero copy" operations on the numerous embedded systems that run the Linux kernel, to enable efficient performance on resource-constrained systems. Usually such embedded systems leverage mechanisms like CMA to carve-out regions of contiguous physical memory for use by bus-mastering peripherals.
This patch adds some logic which determines if user-space is attempting to perform direct I/O mapping on CMA pages, and if so, ensures the mapping succeeds.
This patch is a 1st attempt to allow O_DIRECT on the memory carved-out by CMA. Any suggestions or comments are appreciated!
Signed-off-by: Marc Carino marc.ceeeee@gmail.com --- mm/gup.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/mm/gup.c b/mm/gup.c index cc5a9e7..5fd1bfa 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -353,6 +353,61 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) return 0; }
+static int cma_get_page(struct mm_struct *mm, struct vm_area_struct *vma, + unsigned long start, struct page **page) +{ +#ifdef CONFIG_CMA + const unsigned long pg = start & PAGE_MASK; + unsigned long pfn; + int ret = -EFAULT; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + struct page *tmp_page; + + pgd = pgd_offset(mm, pg); + BUG_ON(pgd_none(*pgd)); + pud = pud_offset(pgd, pg); + BUG_ON(pud_none(*pud)); + pmd = pmd_offset(pud, pg); + if (pmd_none(*pmd)) + return ret; + + pte = pte_offset_map(pmd, pg); + if (!pte) + return ret; + + if (pte_none(*pte)) + goto out; + + tmp_page = pte_page(*pte); + if (!tmp_page) + goto out; + + if (!page_count(tmp_page)) + goto out; + + if (page_mapped(tmp_page)) + goto out; + + if (get_pageblock_migratetype(tmp_page) != MIGRATE_CMA) + goto out; + + if (page) { + *page = tmp_page; + get_page(*page); + } + ret = 0; + +out: + pte_unmap(pte); + return ret; +#else + return 0; +#endif +} + /** * __get_user_pages() - pin user pages in memory * @tsk: task_struct of target task @@ -443,6 +498,16 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, goto next_page; }
+ if (!cma_get_page(mm, vma, start, + pages ? &pages[i] : NULL)) { + if (vmas) + vmas[i] = vma; + i++; + start += PAGE_SIZE; + nr_pages--; + continue; + } + if (!vma || check_vma_flags(vma, gup_flags)) return i ? : -EFAULT; if (is_vm_hugetlb_page(vma)) {
Hello,
On 2014-06-24 11:07, Marc Carino wrote:
It is desirable to perform "zero copy" operations on the numerous embedded systems that run the Linux kernel, to enable efficient performance on resource-constrained systems. Usually such embedded systems leverage mechanisms like CMA to carve-out regions of contiguous physical memory for use by bus-mastering peripherals.
This patch adds some logic which determines if user-space is attempting to perform direct I/O mapping on CMA pages, and if so, ensures the mapping succeeds.
This patch is a 1st attempt to allow O_DIRECT on the memory carved-out by CMA. Any suggestions or comments are appreciated!
Signed-off-by: Marc Carino marc.ceeeee@gmail.com
mm/gup.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/mm/gup.c b/mm/gup.c index cc5a9e7..5fd1bfa 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -353,6 +353,61 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) return 0; } +static int cma_get_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long start, struct page **page)
+{ +#ifdef CONFIG_CMA
- const unsigned long pg = start & PAGE_MASK;
- unsigned long pfn;
- int ret = -EFAULT;
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- struct page *tmp_page;
- pgd = pgd_offset(mm, pg);
- BUG_ON(pgd_none(*pgd));
- pud = pud_offset(pgd, pg);
- BUG_ON(pud_none(*pud));
- pmd = pmd_offset(pud, pg);
- if (pmd_none(*pmd))
return ret;
- pte = pte_offset_map(pmd, pg);
- if (!pte)
return ret;
- if (pte_none(*pte))
goto out;
- tmp_page = pte_page(*pte);
- if (!tmp_page)
goto out;
- if (!page_count(tmp_page))
goto out;
- if (page_mapped(tmp_page))
goto out;
- if (get_pageblock_migratetype(tmp_page) != MIGRATE_CMA)
goto out;
- if (page) {
*page = tmp_page;
get_page(*page);
- }
- ret = 0;
+out:
- pte_unmap(pte);
- return ret;
+#else
- return 0;
+#endif +}
- /**
- __get_user_pages() - pin user pages in memory
- @tsk: task_struct of target task
@@ -443,6 +498,16 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, goto next_page; }
if (!cma_get_page(mm, vma, start,
pages ? &pages[i] : NULL)) {
if (vmas)
vmas[i] = vma;
i++;
start += PAGE_SIZE;
nr_pages--;
continue;
}
if (!vma || check_vma_flags(vma, gup_flags)) return i ? : -EFAULT; if (is_vm_hugetlb_page(vma)) {
Frankly, I have completely no idea how this patch is related to O_DIRECT and how it does. Could you elaborate a bit on the target use case and describe some examples? What problem are you trying to solve?
Best regards
Hi Marek,
Could you elaborate a bit on the target use case and describe some examples?
Sure. The goal is to achieve "zero copy" from a block device (like a SATA drive) into a contiguous buffer, which is mmap'ed from user-space with the O_DIRECT flag. Skipping the buffer cache and DMA'ing directly into a contiguous buffer (which would be initially carved out via CMA) is essential for resource-constrained embedded systems.
What problem are you trying to solve?
On our platform (an UltraHD-capable set-top box), we would like to ensure efficient operation of the system as a whole by ensuring video stream data gets DMA'ed directly into a user-space accessible buffer, with zero copies. Introducing extra CPU memory copies will introduce latency, leading to issues like dropped frames and lower system performance.
I have completely no idea how this patch is related to O_DIRECT and how it does.
When user-space invokes mmap(), the kernel uses get_user_pages() to check for the existence of pages for a particular VMA. In our system, we leverage CMA for contiguous buffers. The missing link was determining whether or not a particular page in the VMA is CMA, and if it has been allocated.
Thanks, Marc
On Tue, Jul 1, 2014 at 3:14 AM, Marek Szyprowski m.szyprowski@samsung.com wrote:
Hello,
On 2014-06-24 11:07, Marc Carino wrote:
It is desirable to perform "zero copy" operations on the numerous embedded systems that run the Linux kernel, to enable efficient performance on resource-constrained systems. Usually such embedded systems leverage mechanisms like CMA to carve-out regions of contiguous physical memory for use by bus-mastering peripherals.
This patch adds some logic which determines if user-space is attempting to perform direct I/O mapping on CMA pages, and if so, ensures the mapping succeeds.
This patch is a 1st attempt to allow O_DIRECT on the memory carved-out by CMA. Any suggestions or comments are appreciated!
Signed-off-by: Marc Carino marc.ceeeee@gmail.com
mm/gup.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/mm/gup.c b/mm/gup.c index cc5a9e7..5fd1bfa 100644 --- a/mm/gup.c +++ b/mm/gup.c @@ -353,6 +353,61 @@ static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags) return 0; } +static int cma_get_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long start, struct page **page)
+{ +#ifdef CONFIG_CMA
const unsigned long pg = start & PAGE_MASK;
unsigned long pfn;
int ret = -EFAULT;
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
struct page *tmp_page;
pgd = pgd_offset(mm, pg);
BUG_ON(pgd_none(*pgd));
pud = pud_offset(pgd, pg);
BUG_ON(pud_none(*pud));
pmd = pmd_offset(pud, pg);
if (pmd_none(*pmd))
return ret;
pte = pte_offset_map(pmd, pg);
if (!pte)
return ret;
if (pte_none(*pte))
goto out;
tmp_page = pte_page(*pte);
if (!tmp_page)
goto out;
if (!page_count(tmp_page))
goto out;
if (page_mapped(tmp_page))
goto out;
if (get_pageblock_migratetype(tmp_page) != MIGRATE_CMA)
goto out;
if (page) {
*page = tmp_page;
get_page(*page);
}
ret = 0;
+out:
pte_unmap(pte);
return ret;
+#else
return 0;
+#endif +}
- /**
- __get_user_pages() - pin user pages in memory
- @tsk: task_struct of target task
@@ -443,6 +498,16 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, goto next_page; }
if (!cma_get_page(mm, vma, start,
pages ? &pages[i] : NULL)) {
if (vmas)
vmas[i] = vma;
i++;
start += PAGE_SIZE;
nr_pages--;
continue;
}
if (!vma || check_vma_flags(vma, gup_flags)) return i ? : -EFAULT; if (is_vm_hugetlb_page(vma)) {
Frankly, I have completely no idea how this patch is related to O_DIRECT and how it does. Could you elaborate a bit on the target use case and describe some examples? What problem are you trying to solve?
Best regards
Marek Szyprowski, PhD Samsung R&D Institute Poland
linaro-mm-sig@lists.linaro.org