aboutsummaryrefslogtreecommitdiff
path: root/patches/vfio.diff
blob: d19da2e24dd451b00a120b231c9e5cee0088d22c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index a3030cd..ab1b82c 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -2019,15 +2019,24 @@ static int vfio_register_iommu_notifier(struct vfio_group *group,
 	int ret;
 
 	ret = vfio_group_add_container_user(group);
-	if (ret)
+	if (ret) {
+		pr_info("vfio_group_add_container_user failed with %d\n", ret);
 		return -EINVAL;
+	}
 
 	container = group->container;
 	driver = container->iommu_driver;
-	if (likely(driver && driver->ops->register_notifier))
+	if (likely(driver && driver->ops->register_notifier)) {
 		ret = driver->ops->register_notifier(container->iommu_data,
-						     events, nb);
-	else
+				events, nb);
+		if (unlikely(!ret) && driver->ops->retro_notify) {
+			ret = driver->ops->retro_notify(container->iommu_data);
+			if (unlikely((ret & NOTIFY_BAD) == NOTIFY_BAD))
+				ret = -ENOTTY;
+			else
+				ret = 0;
+		}
+	} else
 		ret = -ENOTTY;
 
 	vfio_group_try_dissolve_container(group);
@@ -2140,6 +2149,7 @@ int vfio_register_notifier(struct device *dev, enum vfio_notify_type type,
 		ret = vfio_register_group_notifier(group, events, nb);
 		break;
 	default:
+		pr_info("bad notification type %d\n", type);
 		ret = -EINVAL;
 	}
 
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index d0f731c..b47b8f96 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -558,8 +558,10 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
 		return -EINVAL;
 
 	/* Supported for v2 version only */
-	if (!iommu->v2)
+	if (!iommu->v2) {
+		pr_debug("non v2 IOMMU\n");
 		return -EACCES;
+	}
 
 	mutex_lock(&iommu->lock);
 
@@ -1050,6 +1052,30 @@ static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma,
 	return ret;
 }
 
+static int vfio_dma_map_trigger_notifiers(struct vfio_iommu * const iommu,
+		struct vfio_dma const * const dma)
+
+{
+	struct vfio_iommu_type1_dma_map nb_map = {0};
+
+	BUG_ON(!iommu);
+	BUG_ON(!dma);
+
+	nb_map.flags = dma->prot;
+
+	if ((dma->prot & IOMMU_READ) == IOMMU_READ)
+		nb_map.flags |= VFIO_DMA_MAP_FLAG_READ;
+	if ((dma->prot & IOMMU_WRITE) == IOMMU_WRITE)
+		nb_map.flags |= VFIO_DMA_MAP_FLAG_WRITE;
+	nb_map.vaddr = dma->vaddr;
+	nb_map.iova = dma->iova;
+	nb_map.size = dma->size;
+
+	return blocking_notifier_call_chain(&iommu->notifier,
+				    VFIO_IOMMU_NOTIFY_DMA_MAP,
+				    &nb_map);
+}
+
 static int vfio_dma_do_map(struct vfio_iommu *iommu,
 			   struct vfio_iommu_type1_dma_map *map)
 {
@@ -1139,13 +1165,25 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
 	vfio_link_dma(iommu, dma);
 
 	/* Don't pin and map if container doesn't contain IOMMU capable domain*/
-	if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu))
+	if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu)) {
 		dma->size = size;
-	else
+		ret = 0;
+	} else
 		ret = vfio_pin_map_dma(iommu, dma, size);
 
 out_unlock:
 	mutex_unlock(&iommu->lock);
+	/* FIXME is the following safe without having acquired the mutex? */
+	if (!IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) && !ret) {
+		ret = vfio_dma_map_trigger_notifiers(iommu, dma);
+		/* FIXME proceed or clean up and fail? */
+		if ((ret & NOTIFY_BAD) == NOTIFY_BAD) {
+			pr_debug("failed to trigger notifier(s): %d\n", ret);
+			ret = -EINVAL;
+		} else
+			ret = 0;
+	}
+
 	return ret;
 }
 
@@ -1504,8 +1542,11 @@ static void vfio_sanity_check_pfn_list(struct vfio_iommu *iommu)
 
 		dma = rb_entry(n, struct vfio_dma, node);
 
-		if (WARN_ON(!RB_EMPTY_ROOT(&dma->pfn_list)))
+		if (WARN_ON(!RB_EMPTY_ROOT(&dma->pfn_list))) {
+			pr_debug("DMA region %llx-%llx still pinned\n",
+					dma->iova, dma->iova + dma->size);
 			break;
+		}
 	}
 	/* mdev vendor driver must unregister notifier */
 	WARN_ON(iommu->notifier.head);
@@ -1740,7 +1781,7 @@ static int vfio_iommu_type1_register_notifier(void *iommu_data,
 	struct vfio_iommu *iommu = iommu_data;
 
 	/* clear known events */
-	*events &= ~VFIO_IOMMU_NOTIFY_DMA_UNMAP;
+	*events &= ~(VFIO_IOMMU_NOTIFY_DMA_MAP | VFIO_IOMMU_NOTIFY_DMA_UNMAP);
 
 	/* refuse to register if still events remaining */
 	if (*events)
@@ -1749,6 +1790,25 @@ static int vfio_iommu_type1_register_notifier(void *iommu_data,
 	return blocking_notifier_chain_register(&iommu->notifier, nb);
 }
 
+static int vfio_iommu_type1_retro_notify(void *iommu_data)
+{
+	int err = NOTIFY_OK;
+	struct vfio_iommu *iommu;
+	struct vfio_dma *pos, *n;
+
+	BUG_ON(!iommu_data);
+
+	iommu = (struct vfio_iommu*)iommu_data;
+
+	rbtree_postorder_for_each_entry_safe(pos, n, &iommu->dma_list, node) {
+		err = vfio_dma_map_trigger_notifiers(iommu, pos);
+		if ((err & NOTIFY_BAD) == NOTIFY_BAD)
+			break;
+	}
+
+	return err;
+}
+
 static int vfio_iommu_type1_unregister_notifier(void *iommu_data,
 						struct notifier_block *nb)
 {
@@ -1769,6 +1829,7 @@ static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
 	.unpin_pages		= vfio_iommu_type1_unpin_pages,
 	.register_notifier	= vfio_iommu_type1_register_notifier,
 	.unregister_notifier	= vfio_iommu_type1_unregister_notifier,
+	.retro_notify		= vfio_iommu_type1_retro_notify,
 };
 
 static int __init vfio_iommu_type1_init(void)
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 66741ab0..10ee80b 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -85,6 +85,7 @@ struct vfio_iommu_driver_ops {
 					     struct notifier_block *nb);
 	int		(*unregister_notifier)(void *iommu_data,
 					       struct notifier_block *nb);
+	int		(*retro_notify)(void *iommu_data);
 };
 
 extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops);
@@ -118,6 +119,7 @@ enum vfio_notify_type {
 
 /* events for VFIO_IOMMU_NOTIFY */
 #define VFIO_IOMMU_NOTIFY_DMA_UNMAP	BIT(0)
+#define VFIO_IOMMU_NOTIFY_DMA_MAP	BIT(1)
 
 /* events for VFIO_GROUP_NOTIFY */
 #define VFIO_GROUP_NOTIFY_SET_KVM	BIT(0)