Skip to content

Commit f4986a7

Browse files
Steve Sistarejgunthorpe
authored andcommitted
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/[email protected] Suggested-by: Jason Gunthorpe <[email protected]> Signed-off-by: Steve Sistare <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 92687c7 commit f4986a7

File tree

7 files changed

+139
-1
lines changed

7 files changed

+139
-1
lines changed

drivers/iommu/iommufd/io_pagetable.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,14 @@ static int iopt_alloc_area_pages(struct io_pagetable *iopt,
268268
/* Use the first entry to guess the ideal IOVA alignment */
269269
elm = list_first_entry(pages_list, struct iopt_pages_list,
270270
next);
271-
start = elm->start_byte + (uintptr_t)elm->pages->uptr;
271+
switch (elm->pages->type) {
272+
case IOPT_ADDRESS_USER:
273+
start = elm->start_byte + (uintptr_t)elm->pages->uptr;
274+
break;
275+
case IOPT_ADDRESS_FILE:
276+
start = elm->start_byte + elm->pages->start;
277+
break;
278+
}
272279
rc = iopt_alloc_iova(iopt, dst_iova, start, length);
273280
if (rc)
274281
goto out_unlock;
@@ -446,6 +453,33 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
446453
uptr - pages->uptr, iommu_prot, flags);
447454
}
448455

456+
/**
457+
* iopt_map_file_pages() - Like iopt_map_user_pages, but map a file.
458+
* @ictx: iommufd_ctx the iopt is part of
459+
* @iopt: io_pagetable to act on
460+
* @iova: If IOPT_ALLOC_IOVA is set this is unused on input and contains
461+
* the chosen iova on output. Otherwise is the iova to map to on input
462+
* @file: file to map
463+
* @start: map file starting at this byte offset
464+
* @length: Number of bytes to map
465+
* @iommu_prot: Combination of IOMMU_READ/WRITE/etc bits for the mapping
466+
* @flags: IOPT_ALLOC_IOVA or zero
467+
*/
468+
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
469+
unsigned long *iova, struct file *file,
470+
unsigned long start, unsigned long length,
471+
int iommu_prot, unsigned int flags)
472+
{
473+
struct iopt_pages *pages;
474+
475+
pages = iopt_alloc_file_pages(file, start, length,
476+
iommu_prot & IOMMU_WRITE);
477+
if (IS_ERR(pages))
478+
return PTR_ERR(pages);
479+
return iopt_map_common(ictx, iopt, pages, iova, length,
480+
start - pages->start, iommu_prot, flags);
481+
}
482+
449483
struct iova_bitmap_fn_arg {
450484
unsigned long flags;
451485
struct io_pagetable *iopt;

drivers/iommu/iommufd/io_pagetable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ struct iopt_pages {
220220

221221
struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
222222
unsigned long length, bool writable);
223+
struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
224+
unsigned long length, bool writable);
223225
void iopt_release_pages(struct kref *kref);
224226
static inline void iopt_put_pages(struct iopt_pages *pages)
225227
{

drivers/iommu/iommufd/ioas.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/*
33
* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
44
*/
5+
#include <linux/file.h>
56
#include <linux/interval_tree.h>
67
#include <linux/iommu.h>
78
#include <linux/iommufd.h>
@@ -197,6 +198,52 @@ static int conv_iommu_prot(u32 map_flags)
197198
return iommu_prot;
198199
}
199200

201+
int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd)
202+
{
203+
struct iommu_ioas_map_file *cmd = ucmd->cmd;
204+
unsigned long iova = cmd->iova;
205+
struct iommufd_ioas *ioas;
206+
unsigned int flags = 0;
207+
struct file *file;
208+
int rc;
209+
210+
if (cmd->flags &
211+
~(IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE |
212+
IOMMU_IOAS_MAP_READABLE))
213+
return -EOPNOTSUPP;
214+
215+
if (cmd->iova >= ULONG_MAX || cmd->length >= ULONG_MAX)
216+
return -EOVERFLOW;
217+
218+
if (!(cmd->flags &
219+
(IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE)))
220+
return -EINVAL;
221+
222+
ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id);
223+
if (IS_ERR(ioas))
224+
return PTR_ERR(ioas);
225+
226+
if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA))
227+
flags = IOPT_ALLOC_IOVA;
228+
229+
file = fget(cmd->fd);
230+
if (!file)
231+
return -EBADF;
232+
233+
rc = iopt_map_file_pages(ucmd->ictx, &ioas->iopt, &iova, file,
234+
cmd->start, cmd->length,
235+
conv_iommu_prot(cmd->flags), flags);
236+
if (rc)
237+
goto out_put;
238+
239+
cmd->iova = iova;
240+
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
241+
out_put:
242+
iommufd_put_object(ucmd->ictx, &ioas->obj);
243+
fput(file);
244+
return rc;
245+
}
246+
200247
int iommufd_ioas_map(struct iommufd_ucmd *ucmd)
201248
{
202249
struct iommu_ioas_map *cmd = ucmd->cmd;

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
6969
unsigned long *iova, void __user *uptr,
7070
unsigned long length, int iommu_prot,
7171
unsigned int flags);
72+
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
73+
unsigned long *iova, struct file *file,
74+
unsigned long start, unsigned long length,
75+
int iommu_prot, unsigned int flags);
7276
int iopt_map_pages(struct io_pagetable *iopt, struct list_head *pages_list,
7377
unsigned long length, unsigned long *dst_iova,
7478
int iommu_prot, unsigned int flags);
@@ -276,6 +280,7 @@ void iommufd_ioas_destroy(struct iommufd_object *obj);
276280
int iommufd_ioas_iova_ranges(struct iommufd_ucmd *ucmd);
277281
int iommufd_ioas_allow_iovas(struct iommufd_ucmd *ucmd);
278282
int iommufd_ioas_map(struct iommufd_ucmd *ucmd);
283+
int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd);
279284
int iommufd_ioas_copy(struct iommufd_ucmd *ucmd);
280285
int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd);
281286
int iommufd_ioas_option(struct iommufd_ucmd *ucmd);

drivers/iommu/iommufd/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
378378
struct iommu_ioas_iova_ranges, out_iova_alignment),
379379
IOCTL_OP(IOMMU_IOAS_MAP, iommufd_ioas_map, struct iommu_ioas_map,
380380
iova),
381+
IOCTL_OP(IOMMU_IOAS_MAP_FILE, iommufd_ioas_map_file,
382+
struct iommu_ioas_map_file, iova),
381383
IOCTL_OP(IOMMU_IOAS_UNMAP, iommufd_ioas_unmap, struct iommu_ioas_unmap,
382384
length),
383385
IOCTL_OP(IOMMU_OPTION, iommufd_option, struct iommu_option,

drivers/iommu/iommufd/pages.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* last_iova + 1 can overflow. An iopt_pages index will always be much less than
4646
* ULONG_MAX so last_index + 1 cannot overflow.
4747
*/
48+
#include <linux/file.h>
4849
#include <linux/highmem.h>
4950
#include <linux/iommu.h>
5051
#include <linux/iommufd.h>
@@ -1340,6 +1341,26 @@ struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
13401341
return pages;
13411342
}
13421343

1344+
struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
1345+
unsigned long length, bool writable)
1346+
1347+
{
1348+
struct iopt_pages *pages;
1349+
unsigned long start_down = ALIGN_DOWN(start, PAGE_SIZE);
1350+
unsigned long end;
1351+
1352+
if (length && check_add_overflow(start, length - 1, &end))
1353+
return ERR_PTR(-EOVERFLOW);
1354+
1355+
pages = iopt_alloc_pages(start - start_down, length, writable);
1356+
if (IS_ERR(pages))
1357+
return pages;
1358+
pages->file = get_file(file);
1359+
pages->start = start_down;
1360+
pages->type = IOPT_ADDRESS_FILE;
1361+
return pages;
1362+
}
1363+
13431364
void iopt_release_pages(struct kref *kref)
13441365
{
13451366
struct iopt_pages *pages = container_of(kref, struct iopt_pages, kref);
@@ -1352,6 +1373,8 @@ void iopt_release_pages(struct kref *kref)
13521373
mutex_destroy(&pages->mutex);
13531374
put_task_struct(pages->source_task);
13541375
free_uid(pages->source_user);
1376+
if (pages->type == IOPT_ADDRESS_FILE)
1377+
fput(pages->file);
13551378
kfree(pages);
13561379
}
13571380

include/uapi/linux/iommufd.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ enum {
5151
IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP = 0x8c,
5252
IOMMUFD_CMD_HWPT_INVALIDATE = 0x8d,
5353
IOMMUFD_CMD_FAULT_QUEUE_ALLOC = 0x8e,
54+
IOMMUFD_CMD_IOAS_MAP_FILE = 0x8f,
5455
};
5556

5657
/**
@@ -213,6 +214,30 @@ struct iommu_ioas_map {
213214
};
214215
#define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP)
215216

217+
/**
218+
* struct iommu_ioas_map_file - ioctl(IOMMU_IOAS_MAP_FILE)
219+
* @size: sizeof(struct iommu_ioas_map_file)
220+
* @flags: same as for iommu_ioas_map
221+
* @ioas_id: same as for iommu_ioas_map
222+
* @fd: the memfd to map
223+
* @start: byte offset from start of file to map from
224+
* @length: same as for iommu_ioas_map
225+
* @iova: same as for iommu_ioas_map
226+
*
227+
* Set an IOVA mapping from a memfd file. All other arguments and semantics
228+
* match those of IOMMU_IOAS_MAP.
229+
*/
230+
struct iommu_ioas_map_file {
231+
__u32 size;
232+
__u32 flags;
233+
__u32 ioas_id;
234+
__s32 fd;
235+
__aligned_u64 start;
236+
__aligned_u64 length;
237+
__aligned_u64 iova;
238+
};
239+
#define IOMMU_IOAS_MAP_FILE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP_FILE)
240+
216241
/**
217242
* struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY)
218243
* @size: sizeof(struct iommu_ioas_copy)

0 commit comments

Comments
 (0)