mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 10:33:41 +02:00
iommufd: Add IOMMU_IOAS_MAP_FILE
Define the IOMMU_IOAS_MAP_FILE ioctl interface, which allows a user to register memory by passing a memfd plus offset and length. Implement it using the memfd_pin_folios() kAPI. Link: https://patch.msgid.link/r/1729861919-234514-8-git-send-email-steven.sistare@oracle.com Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Steve Sistare <steven.sistare@oracle.com> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
parent
92687c7936
commit
f4986a72d6
|
|
@ -268,7 +268,14 @@ static int iopt_alloc_area_pages(struct io_pagetable *iopt,
|
|||
/* Use the first entry to guess the ideal IOVA alignment */
|
||||
elm = list_first_entry(pages_list, struct iopt_pages_list,
|
||||
next);
|
||||
start = elm->start_byte + (uintptr_t)elm->pages->uptr;
|
||||
switch (elm->pages->type) {
|
||||
case IOPT_ADDRESS_USER:
|
||||
start = elm->start_byte + (uintptr_t)elm->pages->uptr;
|
||||
break;
|
||||
case IOPT_ADDRESS_FILE:
|
||||
start = elm->start_byte + elm->pages->start;
|
||||
break;
|
||||
}
|
||||
rc = iopt_alloc_iova(iopt, dst_iova, start, length);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
|
|
@ -446,6 +453,33 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
|
|||
uptr - pages->uptr, iommu_prot, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* iopt_map_file_pages() - Like iopt_map_user_pages, but map a file.
|
||||
* @ictx: iommufd_ctx the iopt is part of
|
||||
* @iopt: io_pagetable to act on
|
||||
* @iova: If IOPT_ALLOC_IOVA is set this is unused on input and contains
|
||||
* the chosen iova on output. Otherwise is the iova to map to on input
|
||||
* @file: file to map
|
||||
* @start: map file starting at this byte offset
|
||||
* @length: Number of bytes to map
|
||||
* @iommu_prot: Combination of IOMMU_READ/WRITE/etc bits for the mapping
|
||||
* @flags: IOPT_ALLOC_IOVA or zero
|
||||
*/
|
||||
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
|
||||
unsigned long *iova, struct file *file,
|
||||
unsigned long start, unsigned long length,
|
||||
int iommu_prot, unsigned int flags)
|
||||
{
|
||||
struct iopt_pages *pages;
|
||||
|
||||
pages = iopt_alloc_file_pages(file, start, length,
|
||||
iommu_prot & IOMMU_WRITE);
|
||||
if (IS_ERR(pages))
|
||||
return PTR_ERR(pages);
|
||||
return iopt_map_common(ictx, iopt, pages, iova, length,
|
||||
start - pages->start, iommu_prot, flags);
|
||||
}
|
||||
|
||||
struct iova_bitmap_fn_arg {
|
||||
unsigned long flags;
|
||||
struct io_pagetable *iopt;
|
||||
|
|
|
|||
|
|
@ -220,6 +220,8 @@ struct iopt_pages {
|
|||
|
||||
struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
|
||||
unsigned long length, bool writable);
|
||||
struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
|
||||
unsigned long length, bool writable);
|
||||
void iopt_release_pages(struct kref *kref);
|
||||
static inline void iopt_put_pages(struct iopt_pages *pages)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
|
||||
*/
|
||||
#include <linux/file.h>
|
||||
#include <linux/interval_tree.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/iommufd.h>
|
||||
|
|
@ -197,6 +198,52 @@ static int conv_iommu_prot(u32 map_flags)
|
|||
return iommu_prot;
|
||||
}
|
||||
|
||||
int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd)
|
||||
{
|
||||
struct iommu_ioas_map_file *cmd = ucmd->cmd;
|
||||
unsigned long iova = cmd->iova;
|
||||
struct iommufd_ioas *ioas;
|
||||
unsigned int flags = 0;
|
||||
struct file *file;
|
||||
int rc;
|
||||
|
||||
if (cmd->flags &
|
||||
~(IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE |
|
||||
IOMMU_IOAS_MAP_READABLE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (cmd->iova >= ULONG_MAX || cmd->length >= ULONG_MAX)
|
||||
return -EOVERFLOW;
|
||||
|
||||
if (!(cmd->flags &
|
||||
(IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE)))
|
||||
return -EINVAL;
|
||||
|
||||
ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id);
|
||||
if (IS_ERR(ioas))
|
||||
return PTR_ERR(ioas);
|
||||
|
||||
if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA))
|
||||
flags = IOPT_ALLOC_IOVA;
|
||||
|
||||
file = fget(cmd->fd);
|
||||
if (!file)
|
||||
return -EBADF;
|
||||
|
||||
rc = iopt_map_file_pages(ucmd->ictx, &ioas->iopt, &iova, file,
|
||||
cmd->start, cmd->length,
|
||||
conv_iommu_prot(cmd->flags), flags);
|
||||
if (rc)
|
||||
goto out_put;
|
||||
|
||||
cmd->iova = iova;
|
||||
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
|
||||
out_put:
|
||||
iommufd_put_object(ucmd->ictx, &ioas->obj);
|
||||
fput(file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int iommufd_ioas_map(struct iommufd_ucmd *ucmd)
|
||||
{
|
||||
struct iommu_ioas_map *cmd = ucmd->cmd;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
|
|||
unsigned long *iova, void __user *uptr,
|
||||
unsigned long length, int iommu_prot,
|
||||
unsigned int flags);
|
||||
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
|
||||
unsigned long *iova, struct file *file,
|
||||
unsigned long start, unsigned long length,
|
||||
int iommu_prot, unsigned int flags);
|
||||
int iopt_map_pages(struct io_pagetable *iopt, struct list_head *pages_list,
|
||||
unsigned long length, unsigned long *dst_iova,
|
||||
int iommu_prot, unsigned int flags);
|
||||
|
|
@ -276,6 +280,7 @@ void iommufd_ioas_destroy(struct iommufd_object *obj);
|
|||
int iommufd_ioas_iova_ranges(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_allow_iovas(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_map(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_copy(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_option(struct iommufd_ucmd *ucmd);
|
||||
|
|
|
|||
|
|
@ -378,6 +378,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
|
|||
struct iommu_ioas_iova_ranges, out_iova_alignment),
|
||||
IOCTL_OP(IOMMU_IOAS_MAP, iommufd_ioas_map, struct iommu_ioas_map,
|
||||
iova),
|
||||
IOCTL_OP(IOMMU_IOAS_MAP_FILE, iommufd_ioas_map_file,
|
||||
struct iommu_ioas_map_file, iova),
|
||||
IOCTL_OP(IOMMU_IOAS_UNMAP, iommufd_ioas_unmap, struct iommu_ioas_unmap,
|
||||
length),
|
||||
IOCTL_OP(IOMMU_OPTION, iommufd_option, struct iommu_option,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
* last_iova + 1 can overflow. An iopt_pages index will always be much less than
|
||||
* ULONG_MAX so last_index + 1 cannot overflow.
|
||||
*/
|
||||
#include <linux/file.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/iommufd.h>
|
||||
|
|
@ -1340,6 +1341,26 @@ struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
|
|||
return pages;
|
||||
}
|
||||
|
||||
struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
|
||||
unsigned long length, bool writable)
|
||||
|
||||
{
|
||||
struct iopt_pages *pages;
|
||||
unsigned long start_down = ALIGN_DOWN(start, PAGE_SIZE);
|
||||
unsigned long end;
|
||||
|
||||
if (length && check_add_overflow(start, length - 1, &end))
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
|
||||
pages = iopt_alloc_pages(start - start_down, length, writable);
|
||||
if (IS_ERR(pages))
|
||||
return pages;
|
||||
pages->file = get_file(file);
|
||||
pages->start = start_down;
|
||||
pages->type = IOPT_ADDRESS_FILE;
|
||||
return pages;
|
||||
}
|
||||
|
||||
void iopt_release_pages(struct kref *kref)
|
||||
{
|
||||
struct iopt_pages *pages = container_of(kref, struct iopt_pages, kref);
|
||||
|
|
@ -1352,6 +1373,8 @@ void iopt_release_pages(struct kref *kref)
|
|||
mutex_destroy(&pages->mutex);
|
||||
put_task_struct(pages->source_task);
|
||||
free_uid(pages->source_user);
|
||||
if (pages->type == IOPT_ADDRESS_FILE)
|
||||
fput(pages->file);
|
||||
kfree(pages);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ enum {
|
|||
IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP = 0x8c,
|
||||
IOMMUFD_CMD_HWPT_INVALIDATE = 0x8d,
|
||||
IOMMUFD_CMD_FAULT_QUEUE_ALLOC = 0x8e,
|
||||
IOMMUFD_CMD_IOAS_MAP_FILE = 0x8f,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -213,6 +214,30 @@ struct iommu_ioas_map {
|
|||
};
|
||||
#define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_map_file - ioctl(IOMMU_IOAS_MAP_FILE)
|
||||
* @size: sizeof(struct iommu_ioas_map_file)
|
||||
* @flags: same as for iommu_ioas_map
|
||||
* @ioas_id: same as for iommu_ioas_map
|
||||
* @fd: the memfd to map
|
||||
* @start: byte offset from start of file to map from
|
||||
* @length: same as for iommu_ioas_map
|
||||
* @iova: same as for iommu_ioas_map
|
||||
*
|
||||
* Set an IOVA mapping from a memfd file. All other arguments and semantics
|
||||
* match those of IOMMU_IOAS_MAP.
|
||||
*/
|
||||
struct iommu_ioas_map_file {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 ioas_id;
|
||||
__s32 fd;
|
||||
__aligned_u64 start;
|
||||
__aligned_u64 length;
|
||||
__aligned_u64 iova;
|
||||
};
|
||||
#define IOMMU_IOAS_MAP_FILE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP_FILE)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY)
|
||||
* @size: sizeof(struct iommu_ioas_copy)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user