Merge tag 'hwlock-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc...
[platform/kernel/linux-starfive.git] / lib / test_hmm.c
index e3965ca..67e6f83 100644 (file)
@@ -100,6 +100,7 @@ struct dmirror {
 struct dmirror_chunk {
        struct dev_pagemap      pagemap;
        struct dmirror_device   *mdevice;
+       bool remove;
 };
 
 /*
@@ -107,8 +108,8 @@ struct dmirror_chunk {
  */
 struct dmirror_device {
        struct cdev             cdevice;
-       struct hmm_devmem       *devmem;
        unsigned int            zone_device_type;
+       struct device           device;
 
        unsigned int            devmem_capacity;
        unsigned int            devmem_count;
@@ -192,11 +193,15 @@ static int dmirror_fops_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
+static struct dmirror_chunk *dmirror_page_to_chunk(struct page *page)
+{
+       return container_of(page->pgmap, struct dmirror_chunk, pagemap);
+}
+
 static struct dmirror_device *dmirror_page_to_device(struct page *page)
 
 {
-       return container_of(page->pgmap, struct dmirror_chunk,
-                           pagemap)->mdevice;
+       return dmirror_page_to_chunk(page)->mdevice;
 }
 
 static int dmirror_do_fault(struct dmirror *dmirror, struct hmm_range *range)
@@ -627,8 +632,8 @@ static struct page *dmirror_devmem_alloc_page(struct dmirror_device *mdevice)
                        goto error;
        }
 
+       zone_device_page_init(dpage);
        dpage->zone_device_data = rpage;
-       lock_page(dpage);
        return dpage;
 
 error:
@@ -907,7 +912,7 @@ static int dmirror_migrate_to_system(struct dmirror *dmirror,
        struct vm_area_struct *vma;
        unsigned long src_pfns[64] = { 0 };
        unsigned long dst_pfns[64] = { 0 };
-       struct migrate_vma args;
+       struct migrate_vma args = { 0 };
        unsigned long next;
        int ret;
 
@@ -968,7 +973,7 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror,
        unsigned long src_pfns[64] = { 0 };
        unsigned long dst_pfns[64] = { 0 };
        struct dmirror_bounce bounce;
-       struct migrate_vma args;
+       struct migrate_vma args = { 0 };
        unsigned long next;
        int ret;
 
@@ -1218,6 +1223,85 @@ static int dmirror_snapshot(struct dmirror *dmirror,
        return ret;
 }
 
+static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk)
+{
+       unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT;
+       unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT;
+       unsigned long npages = end_pfn - start_pfn + 1;
+       unsigned long i;
+       unsigned long *src_pfns;
+       unsigned long *dst_pfns;
+
+       src_pfns = kcalloc(npages, sizeof(*src_pfns), GFP_KERNEL);
+       dst_pfns = kcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL);
+
+       migrate_device_range(src_pfns, start_pfn, npages);
+       for (i = 0; i < npages; i++) {
+               struct page *dpage, *spage;
+
+               spage = migrate_pfn_to_page(src_pfns[i]);
+               if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE))
+                       continue;
+
+               if (WARN_ON(!is_device_private_page(spage) &&
+                           !is_device_coherent_page(spage)))
+                       continue;
+               spage = BACKING_PAGE(spage);
+               dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL);
+               lock_page(dpage);
+               copy_highpage(dpage, spage);
+               dst_pfns[i] = migrate_pfn(page_to_pfn(dpage));
+               if (src_pfns[i] & MIGRATE_PFN_WRITE)
+                       dst_pfns[i] |= MIGRATE_PFN_WRITE;
+       }
+       migrate_device_pages(src_pfns, dst_pfns, npages);
+       migrate_device_finalize(src_pfns, dst_pfns, npages);
+       kfree(src_pfns);
+       kfree(dst_pfns);
+}
+
+/* Removes free pages from the free list so they can't be re-allocated */
+static void dmirror_remove_free_pages(struct dmirror_chunk *devmem)
+{
+       struct dmirror_device *mdevice = devmem->mdevice;
+       struct page *page;
+
+       for (page = mdevice->free_pages; page; page = page->zone_device_data)
+               if (dmirror_page_to_chunk(page) == devmem)
+                       mdevice->free_pages = page->zone_device_data;
+}
+
+static void dmirror_device_remove_chunks(struct dmirror_device *mdevice)
+{
+       unsigned int i;
+
+       mutex_lock(&mdevice->devmem_lock);
+       if (mdevice->devmem_chunks) {
+               for (i = 0; i < mdevice->devmem_count; i++) {
+                       struct dmirror_chunk *devmem =
+                               mdevice->devmem_chunks[i];
+
+                       spin_lock(&mdevice->lock);
+                       devmem->remove = true;
+                       dmirror_remove_free_pages(devmem);
+                       spin_unlock(&mdevice->lock);
+
+                       dmirror_device_evict_chunk(devmem);
+                       memunmap_pages(&devmem->pagemap);
+                       if (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
+                               release_mem_region(devmem->pagemap.range.start,
+                                                  range_len(&devmem->pagemap.range));
+                       kfree(devmem);
+               }
+               mdevice->devmem_count = 0;
+               mdevice->devmem_capacity = 0;
+               mdevice->free_pages = NULL;
+               kfree(mdevice->devmem_chunks);
+               mdevice->devmem_chunks = NULL;
+       }
+       mutex_unlock(&mdevice->devmem_lock);
+}
+
 static long dmirror_fops_unlocked_ioctl(struct file *filp,
                                        unsigned int command,
                                        unsigned long arg)
@@ -1272,6 +1356,11 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
                ret = dmirror_snapshot(dmirror, &cmd);
                break;
 
+       case HMM_DMIRROR_RELEASE:
+               dmirror_device_remove_chunks(dmirror->mdevice);
+               ret = 0;
+               break;
+
        default:
                return -EINVAL;
        }
@@ -1326,15 +1415,19 @@ static void dmirror_devmem_free(struct page *page)
 
        mdevice = dmirror_page_to_device(page);
        spin_lock(&mdevice->lock);
-       mdevice->cfree++;
-       page->zone_device_data = mdevice->free_pages;
-       mdevice->free_pages = page;
+
+       /* Return page to our allocator if not freeing the chunk */
+       if (!dmirror_page_to_chunk(page)->remove) {
+               mdevice->cfree++;
+               page->zone_device_data = mdevice->free_pages;
+               mdevice->free_pages = page;
+       }
        spin_unlock(&mdevice->lock);
 }
 
 static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
 {
-       struct migrate_vma args;
+       struct migrate_vma args = { 0 };
        unsigned long src_pfns = 0;
        unsigned long dst_pfns = 0;
        struct page *rpage;
@@ -1357,6 +1450,7 @@ static vm_fault_t dmirror_devmem_fault(struct vm_fault *vmf)
        args.dst = &dst_pfns;
        args.pgmap_owner = dmirror->mdevice;
        args.flags = dmirror_select_device(dmirror);
+       args.fault_page = vmf->page;
 
        if (migrate_vma_setup(&args))
                return VM_FAULT_SIGBUS;
@@ -1390,7 +1484,14 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
 
        cdev_init(&mdevice->cdevice, &dmirror_fops);
        mdevice->cdevice.owner = THIS_MODULE;
-       ret = cdev_add(&mdevice->cdevice, dev, 1);
+       device_initialize(&mdevice->device);
+       mdevice->device.devt = dev;
+
+       ret = dev_set_name(&mdevice->device, "hmm_dmirror%u", id);
+       if (ret)
+               return ret;
+
+       ret = cdev_device_add(&mdevice->cdevice, &mdevice->device);
        if (ret)
                return ret;
 
@@ -1400,23 +1501,8 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
 
 static void dmirror_device_remove(struct dmirror_device *mdevice)
 {
-       unsigned int i;
-
-       if (mdevice->devmem_chunks) {
-               for (i = 0; i < mdevice->devmem_count; i++) {
-                       struct dmirror_chunk *devmem =
-                               mdevice->devmem_chunks[i];
-
-                       memunmap_pages(&devmem->pagemap);
-                       if (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
-                               release_mem_region(devmem->pagemap.range.start,
-                                                  range_len(&devmem->pagemap.range));
-                       kfree(devmem);
-               }
-               kfree(mdevice->devmem_chunks);
-       }
-
-       cdev_del(&mdevice->cdevice);
+       dmirror_device_remove_chunks(mdevice);
+       cdev_device_del(&mdevice->cdevice, &mdevice->device);
 }
 
 static int __init hmm_dmirror_init(void)