@@ -30,6 +30,12 @@ struct udmabuf {
struct sg_table *sg;
struct miscdevice *device;
pgoff_t *offsets;
+ struct list_head unpin_list;
+};
+
+struct udmabuf_folio {
+ struct folio *folio;
+ struct list_head list;
};
static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
@@ -153,17 +159,43 @@ static void unmap_udmabuf(struct dma_buf_attachment *at,
return put_sg_table(at->dev, sg, direction);
}
+static void unpin_all_folios(struct list_head *unpin_list)
+{
+ struct udmabuf_folio *ubuf_folio;
+
+ while (!list_empty(unpin_list)) {
+ ubuf_folio = list_first_entry(unpin_list,
+ struct udmabuf_folio, list);
+ unpin_folio(ubuf_folio->folio);
+
+ list_del(&ubuf_folio->list);
+ kfree(ubuf_folio);
+ }
+}
+
+static int add_to_unpin_list(struct list_head *unpin_list,
+ struct folio *folio)
+{
+ struct udmabuf_folio *ubuf_folio;
+
+ ubuf_folio = kzalloc(sizeof(*ubuf_folio), GFP_KERNEL);
+ if (!ubuf_folio)
+ return -ENOMEM;
+
+ ubuf_folio->folio = folio;
+ list_add_tail(&ubuf_folio->list, unpin_list);
+ return 0;
+}
+
static void release_udmabuf(struct dma_buf *buf)
{
struct udmabuf *ubuf = buf->priv;
struct device *dev = ubuf->device->this_device;
- pgoff_t pg;
if (ubuf->sg)
put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL);
- for (pg = 0; pg < ubuf->pagecount; pg++)
- folio_put(ubuf->folios[pg]);
+ unpin_all_folios(&ubuf->unpin_list);
kfree(ubuf->offsets);
kfree(ubuf->folios);
kfree(ubuf);
@@ -218,64 +250,6 @@ static const struct dma_buf_ops udmabuf_ops = {
#define SEALS_WANTED (F_SEAL_SHRINK)
#define SEALS_DENIED (F_SEAL_WRITE)
-static int handle_hugetlb_pages(struct udmabuf *ubuf, struct file *memfd,
- pgoff_t offset, pgoff_t pgcnt,
- pgoff_t *pgbuf)
-{
- struct hstate *hpstate = hstate_file(memfd);
- pgoff_t mapidx = offset >> huge_page_shift(hpstate);
- pgoff_t subpgoff = (offset & ~huge_page_mask(hpstate)) >> PAGE_SHIFT;
- pgoff_t maxsubpgs = huge_page_size(hpstate) >> PAGE_SHIFT;
- struct folio *folio = NULL;
- pgoff_t pgidx;
-
- mapidx <<= huge_page_order(hpstate);
- for (pgidx = 0; pgidx < pgcnt; pgidx++) {
- if (!folio) {
- folio = __filemap_get_folio(memfd->f_mapping,
- mapidx,
- FGP_ACCESSED, 0);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
- }
-
- folio_get(folio);
- ubuf->folios[*pgbuf] = folio;
- ubuf->offsets[*pgbuf] = subpgoff << PAGE_SHIFT;
- (*pgbuf)++;
- if (++subpgoff == maxsubpgs) {
- folio_put(folio);
- folio = NULL;
- subpgoff = 0;
- mapidx += pages_per_huge_page(hpstate);
- }
- }
-
- if (folio)
- folio_put(folio);
-
- return 0;
-}
-
-static int handle_shmem_pages(struct udmabuf *ubuf, struct file *memfd,
- pgoff_t offset, pgoff_t pgcnt,
- pgoff_t *pgbuf)
-{
- pgoff_t pgidx, pgoff = offset >> PAGE_SHIFT;
- struct folio *folio = NULL;
-
- for (pgidx = 0; pgidx < pgcnt; pgidx++) {
- folio = shmem_read_folio(memfd->f_mapping, pgoff + pgidx);
- if (IS_ERR(folio))
- return PTR_ERR(folio);
-
- ubuf->folios[*pgbuf] = folio;
- (*pgbuf)++;
- }
-
- return 0;
-}
-
static int check_memfd_seals(struct file *memfd)
{
int seals;
@@ -321,16 +295,19 @@ static long udmabuf_create(struct miscdevice *device,
struct udmabuf_create_list *head,
struct udmabuf_create_item *list)
{
- pgoff_t pgcnt, pgbuf = 0, pglimit;
+ pgoff_t pgoff, pgcnt, pglimit, pgbuf = 0;
+ long nr_folios, ret = -EINVAL;
struct file *memfd = NULL;
+ struct folio **folios;
struct udmabuf *ubuf;
- int ret = -EINVAL;
- u32 i, flags;
+ u32 i, j, k, flags;
+ loff_t end;
ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL);
if (!ubuf)
return -ENOMEM;
+ INIT_LIST_HEAD(&ubuf->unpin_list);
pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
for (i = 0; i < head->count; i++) {
if (!IS_ALIGNED(list[i].offset, PAGE_SIZE))
@@ -366,17 +343,46 @@ static long udmabuf_create(struct miscdevice *device,
goto err;
pgcnt = list[i].size >> PAGE_SHIFT;
- if (is_file_hugepages(memfd))
- ret = handle_hugetlb_pages(ubuf, memfd,
- list[i].offset,
- pgcnt, &pgbuf);
- else
- ret = handle_shmem_pages(ubuf, memfd,
- list[i].offset,
- pgcnt, &pgbuf);
- if (ret < 0)
+ folios = kmalloc_array(pgcnt, sizeof(*folios), GFP_KERNEL);
+ if (!folios) {
+ ret = -ENOMEM;
goto err;
+ }
+ end = list[i].offset + (pgcnt << PAGE_SHIFT) - 1;
+ ret = memfd_pin_folios(memfd, list[i].offset, end,
+ folios, pgcnt, &pgoff);
+ if (ret <= 0) {
+ kfree(folios);
+ if (!ret)
+ ret = -EINVAL;
+ goto err;
+ }
+
+ nr_folios = ret;
+ pgoff >>= PAGE_SHIFT;
+ for (j = 0, k = 0; j < pgcnt; j++) {
+ ubuf->folios[pgbuf] = folios[k];
+ ubuf->offsets[pgbuf] = pgoff << PAGE_SHIFT;
+
+ if (j == 0 || ubuf->folios[pgbuf-1] != folios[k]) {
+ ret = add_to_unpin_list(&ubuf->unpin_list,
+ folios[k]);
+ if (ret < 0) {
+ kfree(folios);
+ goto err;
+ }
+ }
+
+ pgbuf++;
+ if (++pgoff == folio_nr_pages(folios[k])) {
+ pgoff = 0;
+ if (++k == nr_folios)
+ break;
+ }
+ }
+
+ kfree(folios);
fput(memfd);
memfd = NULL;
}
@@ -389,10 +395,9 @@ static long udmabuf_create(struct miscdevice *device,
return ret;
err:
- while (pgbuf > 0)
- folio_put(ubuf->folios[--pgbuf]);
if (memfd)
fput(memfd);
+ unpin_all_folios(&ubuf->unpin_list);
kfree(ubuf->offsets);
kfree(ubuf->folios);
kfree(ubuf);