@@ -137,6 +137,7 @@ enum hmm_pfn_flag_e {
* result of vmf_insert_pfn() or vm_insert_page(). Therefore, it should not
* be mirrored by a device, because the entry will never have HMM_PFN_VALID
* set and the pfn value is undefined.
+ * HMM_PFN_P2P: this entry have been map as P2P ie the dma address is valid
*
* Driver provide entry value for none entry, error entry and special entry,
* driver can alias (ie use same value for error and special for instance). It
@@ -151,6 +152,7 @@ enum hmm_pfn_value_e {
HMM_PFN_ERROR,
HMM_PFN_NONE,
HMM_PFN_SPECIAL,
+ HMM_PFN_P2P,
HMM_PFN_VALUE_MAX
};
@@ -250,6 +252,8 @@ static inline bool hmm_range_valid(struct hmm_range *range)
static inline struct page *hmm_pfn_to_page(const struct hmm_range *range,
uint64_t pfn)
{
+ if (pfn == range->values[HMM_PFN_P2P])
+ return NULL;
if (pfn == range->values[HMM_PFN_NONE])
return NULL;
if (pfn == range->values[HMM_PFN_ERROR])
@@ -270,6 +274,8 @@ static inline struct page *hmm_pfn_to_page(const struct hmm_range *range,
static inline unsigned long hmm_pfn_to_pfn(const struct hmm_range *range,
uint64_t pfn)
{
+ if (pfn == range->values[HMM_PFN_P2P])
+ return -1UL;
if (pfn == range->values[HMM_PFN_NONE])
return -1UL;
if (pfn == range->values[HMM_PFN_ERROR])
@@ -1058,37 +1058,36 @@ long hmm_range_snapshot(struct hmm_range *range)
}
EXPORT_SYMBOL(hmm_range_snapshot);
-/*
- * hmm_range_fault() - try to fault some address in a virtual address range
- * @range: range being faulted
- * @block: allow blocking on fault (if true it sleeps and do not drop mmap_sem)
- * Returns: 0 on success ortherwise:
- * -EINVAL:
- * Invalid argument
- * -ENOMEM:
- * Out of memory.
- * -EPERM:
- * Invalid permission (for instance asking for write and range
- * is read only).
- * -EAGAIN:
- * If you need to retry and mmap_sem was drop. This can only
- * happens if block argument is false.
- * -EBUSY:
- * If the the range is being invalidated and you should wait for
- * invalidation to finish.
- * -EFAULT:
- * Invalid (ie either no valid vma or it is illegal to access that
- * range), number of valid pages in range->pfns[] (from range start
- * address).
- *
- * This is similar to a regular CPU page fault except that it will not trigger
- * any memory migration if the memory being faulted is not accessible by CPUs
- * and caller does not ask for migration.
- *
- * On error, for one virtual address in the range, the function will mark the
- * corresponding HMM pfn entry with an error flag.
- */
-long hmm_range_fault(struct hmm_range *range, bool block)
+static int hmm_vma_p2p_map(struct hmm_range *range, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end,
+ struct device *device, dma_addr_t *pas)
+{
+ struct hmm_vma_walk hmm_vma_walk;
+ unsigned long npages, i;
+ bool fault, write;
+ uint64_t *pfns;
+ int ret;
+
+ i = (start - range->start) >> PAGE_SHIFT;
+ npages = (end - start) >> PAGE_SHIFT;
+ pfns = &range->pfns[i];
+ pas = &pas[i];
+
+ hmm_vma_walk.range = range;
+ hmm_vma_walk.fault = true;
+ hmm_range_need_fault(&hmm_vma_walk, pfns, npages,
+ 0, &fault, &write);
+
+ ret = vma->vm_ops->p2p_map(vma, device, start, end, pas, write);
+ for (i = 0; i < npages; ++i) {
+ pfns[i] = ret ? range->values[HMM_PFN_ERROR] :
+ range->values[HMM_PFN_P2P];
+ }
+ return ret;
+}
+
+static long _hmm_range_fault(struct hmm_range *range, bool block,
+ struct device *device, dma_addr_t *pas)
{
const unsigned long device_vma = VM_IO | VM_PFNMAP | VM_MIXEDMAP;
unsigned long start = range->start, end;
@@ -1110,9 +1109,22 @@ long hmm_range_fault(struct hmm_range *range, bool block)
}
vma = find_vma(hmm->mm, start);
- if (vma == NULL || (vma->vm_flags & device_vma))
+ if (vma == NULL)
return -EFAULT;
+ end = min(range->end, vma->vm_end);
+ if (vma->vm_flags & device_vma) {
+ if (!device || !pas || !vma->vm_ops->p2p_map)
+ return -EFAULT;
+
+ ret = hmm_vma_p2p_map(range, vma, start,
+ end, device, pas);
+ if (ret)
+ return ret;
+ start = end;
+ continue;
+ }
+
if (is_vm_hugetlb_page(vma)) {
struct hstate *h = hstate_vma(vma);
@@ -1142,7 +1154,6 @@ long hmm_range_fault(struct hmm_range *range, bool block)
hmm_vma_walk.block = block;
hmm_vma_walk.range = range;
mm_walk.private = &hmm_vma_walk;
- end = min(range->end, vma->vm_end);
mm_walk.vma = vma;
mm_walk.mm = vma->vm_mm;
@@ -1175,6 +1186,41 @@ long hmm_range_fault(struct hmm_range *range, bool block)
return (hmm_vma_walk.last - range->start) >> PAGE_SHIFT;
}
+
+/*
+ * hmm_range_fault() - try to fault some address in a virtual address range
+ * @range: range being faulted
+ * @block: allow blocking on fault (if true it sleeps and do not drop mmap_sem)
+ * Returns: 0 on success ortherwise:
+ * -EINVAL:
+ * Invalid argument
+ * -ENOMEM:
+ * Out of memory.
+ * -EPERM:
+ * Invalid permission (for instance asking for write and range
+ * is read only).
+ * -EAGAIN:
+ * If you need to retry and mmap_sem was drop. This can only
+ * happens if block argument is false.
+ * -EBUSY:
+ * If the the range is being invalidated and you should wait for
+ * invalidation to finish.
+ * -EFAULT:
+ * Invalid (ie either no valid vma or it is illegal to access that
+ * range), number of valid pages in range->pfns[] (from range start
+ * address).
+ *
+ * This is similar to a regular CPU page fault except that it will not trigger
+ * any memory migration if the memory being faulted is not accessible by CPUs
+ * and caller does not ask for migration.
+ *
+ * On error, for one virtual address in the range, the function will mark the
+ * corresponding HMM pfn entry with an error flag.
+ */
+long hmm_range_fault(struct hmm_range *range, bool block)
+{
+ return _hmm_range_fault(range, block, NULL, NULL);
+}
EXPORT_SYMBOL(hmm_range_fault);
/*
@@ -1197,7 +1243,7 @@ long hmm_range_dma_map(struct hmm_range *range,
long ret;
again:
- ret = hmm_range_fault(range, block);
+ ret = _hmm_range_fault(range, block, device, daddrs);
if (ret <= 0)
return ret ? ret : -EBUSY;
@@ -1209,6 +1255,11 @@ long hmm_range_dma_map(struct hmm_range *range,
enum dma_data_direction dir = DMA_FROM_DEVICE;
struct page *page;
+ if (range->pfns[i] == range->values[HMM_PFN_P2P]) {
+ mapped++;
+ continue;
+ }
+
/*
* FIXME need to update DMA API to provide invalid DMA address
* value instead of a function to test dma address value. This
@@ -1274,6 +1325,11 @@ long hmm_range_dma_map(struct hmm_range *range,
enum dma_data_direction dir = DMA_FROM_DEVICE;
struct page *page;
+ if (range->pfns[i] == range->values[HMM_PFN_P2P]) {
+ mapped--;
+ continue;
+ }
+
page = hmm_pfn_to_page(range, range->pfns[i]);
if (page == NULL)
continue;
@@ -1305,6 +1361,30 @@ long hmm_range_dma_map(struct hmm_range *range,
}
EXPORT_SYMBOL(hmm_range_dma_map);
+static unsigned long hmm_vma_p2p_unmap(struct hmm_range *range,
+ struct vm_area_struct *vma,
+ unsigned long start,
+ struct device *device,
+ dma_addr_t *pas)
+{
+ unsigned long end;
+
+ if (!vma) {
+ BUG();
+ return 1;
+ }
+
+ start &= PAGE_MASK;
+ if (start < vma->vm_start || start >= vma->vm_end) {
+ BUG();
+ return 1;
+ }
+
+ end = min(range->end, vma->vm_end);
+ vma->vm_ops->p2p_unmap(vma, device, start, end, pas);
+ return (end - start) >> PAGE_SHIFT;
+}
+
/*
* hmm_range_dma_unmap() - unmap range of that was map with hmm_range_dma_map()
* @range: range being unmapped
@@ -1342,6 +1422,14 @@ long hmm_range_dma_unmap(struct hmm_range *range,
enum dma_data_direction dir = DMA_FROM_DEVICE;
struct page *page;
+ if (range->pfns[i] == range->values[HMM_PFN_P2P]) {
+ BUG_ON(!vma);
+ cpages += hmm_vma_p2p_unmap(range, vma, addr,
+ device, &daddrs[i]);
+ i += cpages - 1;
+ continue;
+ }
+
page = hmm_pfn_to_page(range, range->pfns[i]);
if (page == NULL)
continue;