Skip to content

Commit

Permalink
udmabuf: pin the pages using memfd_pin_folios() API
Browse files Browse the repository at this point in the history
Using memfd_pin_folios() will ensure that the pages are pinned
correctly using FOLL_PIN. And, this also ensures that we don't
accidentally break features such as memory hotunplug as it would
not allow pinning pages in the movable zone.

Using this new API also simplifies the code as we no longer have
to deal with extracting individual pages from their mappings or
handle shmem and hugetlb cases separately.

Link: https://lkml.kernel.org/r/20240624063952.1572359-9-vivek.kasireddy@intel.com
Signed-off-by: Vivek Kasireddy <vivek.kasireddy@intel.com>
Acked-by: Dave Airlie <airlied@redhat.com>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Hugh Dickins <hughd@google.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Cc: Junxiao Chang <junxiao.chang@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Vivek Kasireddy authored and Andrew Morton committed Jul 12, 2024
1 parent 5e72b2b commit c6a3194
Showing 1 changed file with 80 additions and 75 deletions.
155 changes: 80 additions & 75 deletions drivers/dma-buf/udmabuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down

0 comments on commit c6a3194

Please sign in to comment.