Skip to content

Commit b659bae

Browse files
apopple-nvidiatorvalds
authored andcommitted
mm: selftests for exclusive device memory
Adds some selftests for exclusive device memory. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Alistair Popple <[email protected]> Acked-by: Jason Gunthorpe <[email protected]> Tested-by: Ralph Campbell <[email protected]> Reviewed-by: Ralph Campbell <[email protected]> Cc: Ben Skeggs <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: John Hubbard <[email protected]> Cc: "Matthew Wilcox (Oracle)" <[email protected]> Cc: Peter Xu <[email protected]> Cc: Shakeel Butt <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent b756a3b commit b659bae

File tree

3 files changed

+285
-0
lines changed

3 files changed

+285
-0
lines changed

lib/test_hmm.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/swapops.h>
2626
#include <linux/sched/mm.h>
2727
#include <linux/platform_device.h>
28+
#include <linux/rmap.h>
2829

2930
#include "test_hmm_uapi.h"
3031

@@ -46,6 +47,7 @@ struct dmirror_bounce {
4647
unsigned long cpages;
4748
};
4849

50+
#define DPT_XA_TAG_ATOMIC 1UL
4951
#define DPT_XA_TAG_WRITE 3UL
5052

5153
/*
@@ -619,6 +621,54 @@ static void dmirror_migrate_alloc_and_copy(struct migrate_vma *args,
619621
}
620622
}
621623

624+
static int dmirror_check_atomic(struct dmirror *dmirror, unsigned long start,
625+
unsigned long end)
626+
{
627+
unsigned long pfn;
628+
629+
for (pfn = start >> PAGE_SHIFT; pfn < (end >> PAGE_SHIFT); pfn++) {
630+
void *entry;
631+
struct page *page;
632+
633+
entry = xa_load(&dmirror->pt, pfn);
634+
page = xa_untag_pointer(entry);
635+
if (xa_pointer_tag(entry) == DPT_XA_TAG_ATOMIC)
636+
return -EPERM;
637+
}
638+
639+
return 0;
640+
}
641+
642+
static int dmirror_atomic_map(unsigned long start, unsigned long end,
643+
struct page **pages, struct dmirror *dmirror)
644+
{
645+
unsigned long pfn, mapped = 0;
646+
int i;
647+
648+
/* Map the migrated pages into the device's page tables. */
649+
mutex_lock(&dmirror->mutex);
650+
651+
for (i = 0, pfn = start >> PAGE_SHIFT; pfn < (end >> PAGE_SHIFT); pfn++, i++) {
652+
void *entry;
653+
654+
if (!pages[i])
655+
continue;
656+
657+
entry = pages[i];
658+
entry = xa_tag_pointer(entry, DPT_XA_TAG_ATOMIC);
659+
entry = xa_store(&dmirror->pt, pfn, entry, GFP_ATOMIC);
660+
if (xa_is_err(entry)) {
661+
mutex_unlock(&dmirror->mutex);
662+
return xa_err(entry);
663+
}
664+
665+
mapped++;
666+
}
667+
668+
mutex_unlock(&dmirror->mutex);
669+
return mapped;
670+
}
671+
622672
static int dmirror_migrate_finalize_and_map(struct migrate_vma *args,
623673
struct dmirror *dmirror)
624674
{
@@ -661,6 +711,72 @@ static int dmirror_migrate_finalize_and_map(struct migrate_vma *args,
661711
return 0;
662712
}
663713

714+
static int dmirror_exclusive(struct dmirror *dmirror,
715+
struct hmm_dmirror_cmd *cmd)
716+
{
717+
unsigned long start, end, addr;
718+
unsigned long size = cmd->npages << PAGE_SHIFT;
719+
struct mm_struct *mm = dmirror->notifier.mm;
720+
struct page *pages[64];
721+
struct dmirror_bounce bounce;
722+
unsigned long next;
723+
int ret;
724+
725+
start = cmd->addr;
726+
end = start + size;
727+
if (end < start)
728+
return -EINVAL;
729+
730+
/* Since the mm is for the mirrored process, get a reference first. */
731+
if (!mmget_not_zero(mm))
732+
return -EINVAL;
733+
734+
mmap_read_lock(mm);
735+
for (addr = start; addr < end; addr = next) {
736+
unsigned long mapped;
737+
int i;
738+
739+
if (end < addr + (ARRAY_SIZE(pages) << PAGE_SHIFT))
740+
next = end;
741+
else
742+
next = addr + (ARRAY_SIZE(pages) << PAGE_SHIFT);
743+
744+
ret = make_device_exclusive_range(mm, addr, next, pages, NULL);
745+
mapped = dmirror_atomic_map(addr, next, pages, dmirror);
746+
for (i = 0; i < ret; i++) {
747+
if (pages[i]) {
748+
unlock_page(pages[i]);
749+
put_page(pages[i]);
750+
}
751+
}
752+
753+
if (addr + (mapped << PAGE_SHIFT) < next) {
754+
mmap_read_unlock(mm);
755+
mmput(mm);
756+
return -EBUSY;
757+
}
758+
}
759+
mmap_read_unlock(mm);
760+
mmput(mm);
761+
762+
/* Return the migrated data for verification. */
763+
ret = dmirror_bounce_init(&bounce, start, size);
764+
if (ret)
765+
return ret;
766+
mutex_lock(&dmirror->mutex);
767+
ret = dmirror_do_read(dmirror, start, end, &bounce);
768+
mutex_unlock(&dmirror->mutex);
769+
if (ret == 0) {
770+
if (copy_to_user(u64_to_user_ptr(cmd->ptr), bounce.ptr,
771+
bounce.size))
772+
ret = -EFAULT;
773+
}
774+
775+
cmd->cpages = bounce.cpages;
776+
dmirror_bounce_fini(&bounce);
777+
return ret;
778+
}
779+
664780
static int dmirror_migrate(struct dmirror *dmirror,
665781
struct hmm_dmirror_cmd *cmd)
666782
{
@@ -948,6 +1064,15 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
9481064
ret = dmirror_migrate(dmirror, &cmd);
9491065
break;
9501066

1067+
case HMM_DMIRROR_EXCLUSIVE:
1068+
ret = dmirror_exclusive(dmirror, &cmd);
1069+
break;
1070+
1071+
case HMM_DMIRROR_CHECK_EXCLUSIVE:
1072+
ret = dmirror_check_atomic(dmirror, cmd.addr,
1073+
cmd.addr + (cmd.npages << PAGE_SHIFT));
1074+
break;
1075+
9511076
case HMM_DMIRROR_SNAPSHOT:
9521077
ret = dmirror_snapshot(dmirror, &cmd);
9531078
break;

lib/test_hmm_uapi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ struct hmm_dmirror_cmd {
3333
#define HMM_DMIRROR_WRITE _IOWR('H', 0x01, struct hmm_dmirror_cmd)
3434
#define HMM_DMIRROR_MIGRATE _IOWR('H', 0x02, struct hmm_dmirror_cmd)
3535
#define HMM_DMIRROR_SNAPSHOT _IOWR('H', 0x03, struct hmm_dmirror_cmd)
36+
#define HMM_DMIRROR_EXCLUSIVE _IOWR('H', 0x04, struct hmm_dmirror_cmd)
37+
#define HMM_DMIRROR_CHECK_EXCLUSIVE _IOWR('H', 0x05, struct hmm_dmirror_cmd)
3638

3739
/*
3840
* Values returned in hmm_dmirror_cmd.ptr for HMM_DMIRROR_SNAPSHOT.

tools/testing/selftests/vm/hmm-tests.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,4 +1485,162 @@ TEST_F(hmm2, double_map)
14851485
hmm_buffer_free(buffer);
14861486
}
14871487

1488+
/*
1489+
* Basic check of exclusive faulting.
1490+
*/
1491+
TEST_F(hmm, exclusive)
1492+
{
1493+
struct hmm_buffer *buffer;
1494+
unsigned long npages;
1495+
unsigned long size;
1496+
unsigned long i;
1497+
int *ptr;
1498+
int ret;
1499+
1500+
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
1501+
ASSERT_NE(npages, 0);
1502+
size = npages << self->page_shift;
1503+
1504+
buffer = malloc(sizeof(*buffer));
1505+
ASSERT_NE(buffer, NULL);
1506+
1507+
buffer->fd = -1;
1508+
buffer->size = size;
1509+
buffer->mirror = malloc(size);
1510+
ASSERT_NE(buffer->mirror, NULL);
1511+
1512+
buffer->ptr = mmap(NULL, size,
1513+
PROT_READ | PROT_WRITE,
1514+
MAP_PRIVATE | MAP_ANONYMOUS,
1515+
buffer->fd, 0);
1516+
ASSERT_NE(buffer->ptr, MAP_FAILED);
1517+
1518+
/* Initialize buffer in system memory. */
1519+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1520+
ptr[i] = i;
1521+
1522+
/* Map memory exclusively for device access. */
1523+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
1524+
ASSERT_EQ(ret, 0);
1525+
ASSERT_EQ(buffer->cpages, npages);
1526+
1527+
/* Check what the device read. */
1528+
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
1529+
ASSERT_EQ(ptr[i], i);
1530+
1531+
/* Fault pages back to system memory and check them. */
1532+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1533+
ASSERT_EQ(ptr[i]++, i);
1534+
1535+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1536+
ASSERT_EQ(ptr[i], i+1);
1537+
1538+
/* Check atomic access revoked */
1539+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_CHECK_EXCLUSIVE, buffer, npages);
1540+
ASSERT_EQ(ret, 0);
1541+
1542+
hmm_buffer_free(buffer);
1543+
}
1544+
1545+
TEST_F(hmm, exclusive_mprotect)
1546+
{
1547+
struct hmm_buffer *buffer;
1548+
unsigned long npages;
1549+
unsigned long size;
1550+
unsigned long i;
1551+
int *ptr;
1552+
int ret;
1553+
1554+
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
1555+
ASSERT_NE(npages, 0);
1556+
size = npages << self->page_shift;
1557+
1558+
buffer = malloc(sizeof(*buffer));
1559+
ASSERT_NE(buffer, NULL);
1560+
1561+
buffer->fd = -1;
1562+
buffer->size = size;
1563+
buffer->mirror = malloc(size);
1564+
ASSERT_NE(buffer->mirror, NULL);
1565+
1566+
buffer->ptr = mmap(NULL, size,
1567+
PROT_READ | PROT_WRITE,
1568+
MAP_PRIVATE | MAP_ANONYMOUS,
1569+
buffer->fd, 0);
1570+
ASSERT_NE(buffer->ptr, MAP_FAILED);
1571+
1572+
/* Initialize buffer in system memory. */
1573+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1574+
ptr[i] = i;
1575+
1576+
/* Map memory exclusively for device access. */
1577+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
1578+
ASSERT_EQ(ret, 0);
1579+
ASSERT_EQ(buffer->cpages, npages);
1580+
1581+
/* Check what the device read. */
1582+
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
1583+
ASSERT_EQ(ptr[i], i);
1584+
1585+
ret = mprotect(buffer->ptr, size, PROT_READ);
1586+
ASSERT_EQ(ret, 0);
1587+
1588+
/* Simulate a device writing system memory. */
1589+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
1590+
ASSERT_EQ(ret, -EPERM);
1591+
1592+
hmm_buffer_free(buffer);
1593+
}
1594+
1595+
/*
1596+
* Check copy-on-write works.
1597+
*/
1598+
TEST_F(hmm, exclusive_cow)
1599+
{
1600+
struct hmm_buffer *buffer;
1601+
unsigned long npages;
1602+
unsigned long size;
1603+
unsigned long i;
1604+
int *ptr;
1605+
int ret;
1606+
1607+
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
1608+
ASSERT_NE(npages, 0);
1609+
size = npages << self->page_shift;
1610+
1611+
buffer = malloc(sizeof(*buffer));
1612+
ASSERT_NE(buffer, NULL);
1613+
1614+
buffer->fd = -1;
1615+
buffer->size = size;
1616+
buffer->mirror = malloc(size);
1617+
ASSERT_NE(buffer->mirror, NULL);
1618+
1619+
buffer->ptr = mmap(NULL, size,
1620+
PROT_READ | PROT_WRITE,
1621+
MAP_PRIVATE | MAP_ANONYMOUS,
1622+
buffer->fd, 0);
1623+
ASSERT_NE(buffer->ptr, MAP_FAILED);
1624+
1625+
/* Initialize buffer in system memory. */
1626+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1627+
ptr[i] = i;
1628+
1629+
/* Map memory exclusively for device access. */
1630+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages);
1631+
ASSERT_EQ(ret, 0);
1632+
ASSERT_EQ(buffer->cpages, npages);
1633+
1634+
fork();
1635+
1636+
/* Fault pages back to system memory and check them. */
1637+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1638+
ASSERT_EQ(ptr[i]++, i);
1639+
1640+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1641+
ASSERT_EQ(ptr[i], i+1);
1642+
1643+
hmm_buffer_free(buffer);
1644+
}
1645+
14881646
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)