Skip to content

Commit ad4c365

Browse files
apopple-nvidiaakpm00
authored andcommitted
hmm-tests: add test for migrate_device_range()
Link: https://lkml.kernel.org/r/a73cf109de0224cfd118d22be58ddebac3ae2897.1664366292.git-series.apopple@nvidia.com Signed-off-by: Alistair Popple <[email protected]> Cc: Jason Gunthorpe <[email protected]> Cc: Ralph Campbell <[email protected]> Cc: John Hubbard <[email protected]> Cc: Alex Sierra <[email protected]> Cc: Felix Kuehling <[email protected]> Cc: Alex Deucher <[email protected]> Cc: Ben Skeggs <[email protected]> Cc: Christian König <[email protected]> Cc: Dan Williams <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: "Huang, Ying" <[email protected]> Cc: Lyude Paul <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Yang Shi <[email protected]> Cc: Zi Yan <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 2498812 commit ad4c365

File tree

3 files changed

+149
-21
lines changed

3 files changed

+149
-21
lines changed

lib/test_hmm.c

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ struct dmirror {
100100
struct dmirror_chunk {
101101
struct dev_pagemap pagemap;
102102
struct dmirror_device *mdevice;
103+
bool remove;
103104
};
104105

105106
/*
@@ -192,11 +193,15 @@ static int dmirror_fops_release(struct inode *inode, struct file *filp)
192193
return 0;
193194
}
194195

196+
static struct dmirror_chunk *dmirror_page_to_chunk(struct page *page)
197+
{
198+
return container_of(page->pgmap, struct dmirror_chunk, pagemap);
199+
}
200+
195201
static struct dmirror_device *dmirror_page_to_device(struct page *page)
196202

197203
{
198-
return container_of(page->pgmap, struct dmirror_chunk,
199-
pagemap)->mdevice;
204+
return dmirror_page_to_chunk(page)->mdevice;
200205
}
201206

202207
static int dmirror_do_fault(struct dmirror *dmirror, struct hmm_range *range)
@@ -1218,6 +1223,85 @@ static int dmirror_snapshot(struct dmirror *dmirror,
12181223
return ret;
12191224
}
12201225

1226+
static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk)
1227+
{
1228+
unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT;
1229+
unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT;
1230+
unsigned long npages = end_pfn - start_pfn + 1;
1231+
unsigned long i;
1232+
unsigned long *src_pfns;
1233+
unsigned long *dst_pfns;
1234+
1235+
src_pfns = kcalloc(npages, sizeof(*src_pfns), GFP_KERNEL);
1236+
dst_pfns = kcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL);
1237+
1238+
migrate_device_range(src_pfns, start_pfn, npages);
1239+
for (i = 0; i < npages; i++) {
1240+
struct page *dpage, *spage;
1241+
1242+
spage = migrate_pfn_to_page(src_pfns[i]);
1243+
if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE))
1244+
continue;
1245+
1246+
if (WARN_ON(!is_device_private_page(spage) &&
1247+
!is_device_coherent_page(spage)))
1248+
continue;
1249+
spage = BACKING_PAGE(spage);
1250+
dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL);
1251+
lock_page(dpage);
1252+
copy_highpage(dpage, spage);
1253+
dst_pfns[i] = migrate_pfn(page_to_pfn(dpage));
1254+
if (src_pfns[i] & MIGRATE_PFN_WRITE)
1255+
dst_pfns[i] |= MIGRATE_PFN_WRITE;
1256+
}
1257+
migrate_device_pages(src_pfns, dst_pfns, npages);
1258+
migrate_device_finalize(src_pfns, dst_pfns, npages);
1259+
kfree(src_pfns);
1260+
kfree(dst_pfns);
1261+
}
1262+
1263+
/* Removes free pages from the free list so they can't be re-allocated */
1264+
static void dmirror_remove_free_pages(struct dmirror_chunk *devmem)
1265+
{
1266+
struct dmirror_device *mdevice = devmem->mdevice;
1267+
struct page *page;
1268+
1269+
for (page = mdevice->free_pages; page; page = page->zone_device_data)
1270+
if (dmirror_page_to_chunk(page) == devmem)
1271+
mdevice->free_pages = page->zone_device_data;
1272+
}
1273+
1274+
static void dmirror_device_remove_chunks(struct dmirror_device *mdevice)
1275+
{
1276+
unsigned int i;
1277+
1278+
mutex_lock(&mdevice->devmem_lock);
1279+
if (mdevice->devmem_chunks) {
1280+
for (i = 0; i < mdevice->devmem_count; i++) {
1281+
struct dmirror_chunk *devmem =
1282+
mdevice->devmem_chunks[i];
1283+
1284+
spin_lock(&mdevice->lock);
1285+
devmem->remove = true;
1286+
dmirror_remove_free_pages(devmem);
1287+
spin_unlock(&mdevice->lock);
1288+
1289+
dmirror_device_evict_chunk(devmem);
1290+
memunmap_pages(&devmem->pagemap);
1291+
if (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
1292+
release_mem_region(devmem->pagemap.range.start,
1293+
range_len(&devmem->pagemap.range));
1294+
kfree(devmem);
1295+
}
1296+
mdevice->devmem_count = 0;
1297+
mdevice->devmem_capacity = 0;
1298+
mdevice->free_pages = NULL;
1299+
kfree(mdevice->devmem_chunks);
1300+
mdevice->devmem_chunks = NULL;
1301+
}
1302+
mutex_unlock(&mdevice->devmem_lock);
1303+
}
1304+
12211305
static long dmirror_fops_unlocked_ioctl(struct file *filp,
12221306
unsigned int command,
12231307
unsigned long arg)
@@ -1272,6 +1356,11 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
12721356
ret = dmirror_snapshot(dmirror, &cmd);
12731357
break;
12741358

1359+
case HMM_DMIRROR_RELEASE:
1360+
dmirror_device_remove_chunks(dmirror->mdevice);
1361+
ret = 0;
1362+
break;
1363+
12751364
default:
12761365
return -EINVAL;
12771366
}
@@ -1326,9 +1415,13 @@ static void dmirror_devmem_free(struct page *page)
13261415

13271416
mdevice = dmirror_page_to_device(page);
13281417
spin_lock(&mdevice->lock);
1329-
mdevice->cfree++;
1330-
page->zone_device_data = mdevice->free_pages;
1331-
mdevice->free_pages = page;
1418+
1419+
/* Return page to our allocator if not freeing the chunk */
1420+
if (!dmirror_page_to_chunk(page)->remove) {
1421+
mdevice->cfree++;
1422+
page->zone_device_data = mdevice->free_pages;
1423+
mdevice->free_pages = page;
1424+
}
13321425
spin_unlock(&mdevice->lock);
13331426
}
13341427

@@ -1408,22 +1501,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
14081501

14091502
static void dmirror_device_remove(struct dmirror_device *mdevice)
14101503
{
1411-
unsigned int i;
1412-
1413-
if (mdevice->devmem_chunks) {
1414-
for (i = 0; i < mdevice->devmem_count; i++) {
1415-
struct dmirror_chunk *devmem =
1416-
mdevice->devmem_chunks[i];
1417-
1418-
memunmap_pages(&devmem->pagemap);
1419-
if (devmem->pagemap.type == MEMORY_DEVICE_PRIVATE)
1420-
release_mem_region(devmem->pagemap.range.start,
1421-
range_len(&devmem->pagemap.range));
1422-
kfree(devmem);
1423-
}
1424-
kfree(mdevice->devmem_chunks);
1425-
}
1426-
1504+
dmirror_device_remove_chunks(mdevice);
14271505
cdev_device_del(&mdevice->cdevice, &mdevice->device);
14281506
}
14291507

lib/test_hmm_uapi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct hmm_dmirror_cmd {
3636
#define HMM_DMIRROR_SNAPSHOT _IOWR('H', 0x04, struct hmm_dmirror_cmd)
3737
#define HMM_DMIRROR_EXCLUSIVE _IOWR('H', 0x05, struct hmm_dmirror_cmd)
3838
#define HMM_DMIRROR_CHECK_EXCLUSIVE _IOWR('H', 0x06, struct hmm_dmirror_cmd)
39+
#define HMM_DMIRROR_RELEASE _IOWR('H', 0x07, struct hmm_dmirror_cmd)
3940

4041
/*
4142
* Values returned in hmm_dmirror_cmd.ptr for HMM_DMIRROR_SNAPSHOT.

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,55 @@ TEST_F(hmm, migrate_fault)
10541054
hmm_buffer_free(buffer);
10551055
}
10561056

1057+
TEST_F(hmm, migrate_release)
1058+
{
1059+
struct hmm_buffer *buffer;
1060+
unsigned long npages;
1061+
unsigned long size;
1062+
unsigned long i;
1063+
int *ptr;
1064+
int ret;
1065+
1066+
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
1067+
ASSERT_NE(npages, 0);
1068+
size = npages << self->page_shift;
1069+
1070+
buffer = malloc(sizeof(*buffer));
1071+
ASSERT_NE(buffer, NULL);
1072+
1073+
buffer->fd = -1;
1074+
buffer->size = size;
1075+
buffer->mirror = malloc(size);
1076+
ASSERT_NE(buffer->mirror, NULL);
1077+
1078+
buffer->ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
1079+
MAP_PRIVATE | MAP_ANONYMOUS, buffer->fd, 0);
1080+
ASSERT_NE(buffer->ptr, MAP_FAILED);
1081+
1082+
/* Initialize buffer in system memory. */
1083+
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
1084+
ptr[i] = i;
1085+
1086+
/* Migrate memory to device. */
1087+
ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages);
1088+
ASSERT_EQ(ret, 0);
1089+
ASSERT_EQ(buffer->cpages, npages);
1090+
1091+
/* Check what the device read. */
1092+
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
1093+
ASSERT_EQ(ptr[i], i);
1094+
1095+
/* Release device memory. */
1096+
ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_RELEASE, buffer, npages);
1097+
ASSERT_EQ(ret, 0);
1098+
1099+
/* Fault pages back to system memory and check them. */
1100+
for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i)
1101+
ASSERT_EQ(ptr[i], i);
1102+
1103+
hmm_buffer_free(buffer);
1104+
}
1105+
10571106
/*
10581107
* Migrate anonymous shared memory to device private memory.
10591108
*/

0 commit comments

Comments
 (0)