diff --git a/drivers/vfio/container.c b/drivers/vfio/container.c
index d53d08f16973..b60fcbf7bad0 100644
--- a/drivers/vfio/container.c
+++ b/drivers/vfio/container.c
@@ -11,6 +11,7 @@
 #include <linux/iommu.h>
 #include <linux/miscdevice.h>
 #include <linux/vfio.h>
+#include <linux/pkernfs.h>
 #include <uapi/linux/vfio.h>
 
 #include "vfio.h"
@@ -21,6 +22,7 @@ struct vfio_container {
 	struct rw_semaphore		group_lock;
 	struct vfio_iommu_driver	*iommu_driver;
 	void				*iommu_data;
+	struct file			*persistent_pgtables;
 	bool				noiommu;
 };
 
@@ -306,6 +308,8 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
 			continue;
 		}
 
+		driver->ops->set_persistent_pgtables(data, container->persistent_pgtables);
+
 		ret = __vfio_container_attach_groups(container, driver, data);
 		if (ret) {
 			driver->ops->release(data);
@@ -324,6 +328,26 @@ static long vfio_ioctl_set_iommu(struct vfio_container *container,
 	return ret;
 }
 
+static int vfio_ioctl_set_persistent_pgtables(struct vfio_container *container,
+		unsigned long arg)
+{
+	struct vfio_set_persistent_pgtables set_ppts;
+	struct file *ppts;
+
+	if (copy_from_user(&set_ppts, (void __user *)arg, sizeof(set_ppts)))
+		return -EFAULT;
+
+	ppts = fget(set_ppts.persistent_pgtables_fd);
+	if (!ppts)
+		return -EBADF;
+	if (!pkernfs_is_iommu_domain_pgtables(ppts)) {
+		fput(ppts);
+		return -EBADF;
+	}
+	container->persistent_pgtables = ppts;
+	return 0;
+}
+
 static long vfio_fops_unl_ioctl(struct file *filep,
 				unsigned int cmd, unsigned long arg)
 {
@@ -345,6 +369,9 @@ static long vfio_fops_unl_ioctl(struct file *filep,
 	case VFIO_SET_IOMMU:
 		ret = vfio_ioctl_set_iommu(container, arg);
 		break;
+	case VFIO_CONTAINER_SET_PERSISTENT_PGTABLES:
+		ret = vfio_ioctl_set_persistent_pgtables(container, arg);
+		break;
 	default:
 		driver = container->iommu_driver;
 		data = container->iommu_data;
diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
index 307e3f29b527..6fa301bf6474 100644
--- a/drivers/vfio/vfio.h
+++ b/drivers/vfio/vfio.h
@@ -226,6 +226,8 @@ struct vfio_iommu_driver_ops {
 				  void *data, size_t count, bool write);
 	struct iommu_domain *(*group_iommu_domain)(void *iommu_data,
 						   struct iommu_group *group);
+	int		(*set_persistent_pgtables)(void *iommu_data,
+						   struct file *ppts);
 };
 
 struct vfio_iommu_driver {
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index eacd6ec04de5..b36edfc5c9ef 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -75,6 +75,7 @@ struct vfio_iommu {
 	bool			nesting;
 	bool			dirty_page_tracking;
 	struct list_head	emulated_iommu_groups;
+	struct file		*persistent_pgtables;
 };
 
 struct vfio_domain {
@@ -2143,9 +2144,14 @@ static void vfio_iommu_iova_insert_copy(struct vfio_iommu *iommu,
 
 static int vfio_iommu_domain_alloc(struct device *dev, void *data)
 {
+	/* data is an in pointer to PPTs, and an out to the new domain. */
+	struct file *ppts = *(struct file **) data;
 	struct iommu_domain **domain = data;
 
-	*domain = iommu_domain_alloc(dev->bus);
+	if (ppts)
+		*domain = iommu_domain_alloc_persistent(dev->bus, ppts);
+	else
+		*domain = iommu_domain_alloc(dev->bus);
 	return 1; /* Don't iterate */
 }
 
@@ -2156,6 +2162,8 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 	struct vfio_iommu_group *group;
 	struct vfio_domain *domain, *d;
 	bool resv_msi;
+	/* In/out ptr to iommu_domain_alloc. */
+	void *domain_alloc_data;
 	phys_addr_t resv_msi_base = 0;
 	struct iommu_domain_geometry *geo;
 	LIST_HEAD(iova_copy);
@@ -2203,8 +2211,12 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
 	 * want to iterate beyond the first device (if any).
 	 */
 	ret = -EIO;
-	iommu_group_for_each_dev(iommu_group, &domain->domain,
+	/* Smuggle the PPTs in the data field; it will be clobbered with the new domain */
+	domain_alloc_data = iommu->persistent_pgtables;
+	iommu_group_for_each_dev(iommu_group, &domain_alloc_data,
 				 vfio_iommu_domain_alloc);
+	domain->domain = domain_alloc_data;
+
 	if (!domain->domain)
 		goto out_free_domain;
 
@@ -3165,6 +3177,16 @@ vfio_iommu_type1_group_iommu_domain(void *iommu_data,
 	return domain;
 }
 
+int vfio_iommu_type1_set_persistent_pgtables(void *iommu_data,
+				    struct file *ppts)
+{
+
+	struct vfio_iommu *iommu = iommu_data;
+
+	iommu->persistent_pgtables = ppts;
+	return 0;
+}
+
 static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
 	.name			= "vfio-iommu-type1",
 	.owner			= THIS_MODULE,
@@ -3179,6 +3201,7 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
 	.unregister_device	= vfio_iommu_type1_unregister_device,
 	.dma_rw			= vfio_iommu_type1_dma_rw,
 	.group_iommu_domain	= vfio_iommu_type1_group_iommu_domain,
+	.set_persistent_pgtables = vfio_iommu_type1_set_persistent_pgtables,
 };
 
 static int __init vfio_iommu_type1_init(void)
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index afc1369216d9..fa9676bb4b26 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1797,6 +1797,15 @@ struct vfio_iommu_spapr_tce_remove {
 };
 #define VFIO_IOMMU_SPAPR_TCE_REMOVE	_IO(VFIO_TYPE, VFIO_BASE + 20)
 
+struct vfio_set_persistent_pgtables {
+	/*
+	 * File descriptor for a pkernfs IOMMU pgtables
+	 * file to be used for persistence.
+	 */
+	__u32	persistent_pgtables_fd;
+};
+#define VFIO_CONTAINER_SET_PERSISTENT_PGTABLES	_IO(VFIO_TYPE, VFIO_BASE + 21)
+
 /* ***************************************************************** */
 
 #endif /* _UAPIVFIO_H */
