aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iommu/amd_iommu_v2.c
diff options
context:
space:
mode:
authorJoerg Roedel <joerg.roedel@amd.com>2011-11-24 04:41:57 -0500
committerJoerg Roedel <joerg.roedel@amd.com>2011-12-12 09:34:42 -0500
commit2d5503b624736abfe0e0bad281f9b8d8a705b930 (patch)
tree6ed415d39c6ac928dbb2997bb86f612b9b87b766 /drivers/iommu/amd_iommu_v2.c
parented96f228ba9725edf69385bffdc19ee5bb0ec641 (diff)
iommu/amd: Add routines to bind/unbind a pasid
This patch adds routines to bind a specific process address-space to a given PASID. Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Diffstat (limited to 'drivers/iommu/amd_iommu_v2.c')
-rw-r--r--drivers/iommu/amd_iommu_v2.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index bfceed25c186..b5ee09ece651 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -19,6 +19,7 @@
19#include <linux/amd-iommu.h> 19#include <linux/amd-iommu.h>
20#include <linux/mm_types.h> 20#include <linux/mm_types.h>
21#include <linux/module.h> 21#include <linux/module.h>
22#include <linux/sched.h>
22#include <linux/iommu.h> 23#include <linux/iommu.h>
23#include <linux/pci.h> 24#include <linux/pci.h>
24#include <linux/gfp.h> 25#include <linux/gfp.h>
@@ -61,6 +62,10 @@ static spinlock_t state_lock;
61 62
62/* List and lock for all pasid_states */ 63/* List and lock for all pasid_states */
63static LIST_HEAD(pasid_state_list); 64static LIST_HEAD(pasid_state_list);
65static DEFINE_SPINLOCK(ps_lock);
66
67static void free_pasid_states(struct device_state *dev_state);
68static void unbind_pasid(struct device_state *dev_state, int pasid);
64 69
65static u16 device_id(struct pci_dev *pdev) 70static u16 device_id(struct pci_dev *pdev)
66{ 71{
@@ -88,8 +93,16 @@ static struct device_state *get_device_state(u16 devid)
88 93
89static void free_device_state(struct device_state *dev_state) 94static void free_device_state(struct device_state *dev_state)
90{ 95{
96 /*
97 * First detach device from domain - No more PRI requests will arrive
98 * from that device after it is unbound from the IOMMUv2 domain.
99 */
91 iommu_detach_device(dev_state->domain, &dev_state->pdev->dev); 100 iommu_detach_device(dev_state->domain, &dev_state->pdev->dev);
101
102 /* Everything is down now, free the IOMMUv2 domain */
92 iommu_domain_free(dev_state->domain); 103 iommu_domain_free(dev_state->domain);
104
105 /* Finally get rid of the device-state */
93 kfree(dev_state); 106 kfree(dev_state);
94} 107}
95 108
@@ -99,6 +112,296 @@ static void put_device_state(struct device_state *dev_state)
99 free_device_state(dev_state); 112 free_device_state(dev_state);
100} 113}
101 114
115static void link_pasid_state(struct pasid_state *pasid_state)
116{
117 spin_lock(&ps_lock);
118 list_add_tail(&pasid_state->list, &pasid_state_list);
119 spin_unlock(&ps_lock);
120}
121
122static void __unlink_pasid_state(struct pasid_state *pasid_state)
123{
124 list_del(&pasid_state->list);
125}
126
127static void unlink_pasid_state(struct pasid_state *pasid_state)
128{
129 spin_lock(&ps_lock);
130 __unlink_pasid_state(pasid_state);
131 spin_unlock(&ps_lock);
132}
133
134/* Must be called under dev_state->lock */
135static struct pasid_state **__get_pasid_state_ptr(struct device_state *dev_state,
136 int pasid, bool alloc)
137{
138 struct pasid_state **root, **ptr;
139 int level, index;
140
141 level = dev_state->pasid_levels;
142 root = dev_state->states;
143
144 while (true) {
145
146 index = (pasid >> (9 * level)) & 0x1ff;
147 ptr = &root[index];
148
149 if (level == 0)
150 break;
151
152 if (*ptr == NULL) {
153 if (!alloc)
154 return NULL;
155
156 *ptr = (void *)get_zeroed_page(GFP_ATOMIC);
157 if (*ptr == NULL)
158 return NULL;
159 }
160
161 root = (struct pasid_state **)*ptr;
162 level -= 1;
163 }
164
165 return ptr;
166}
167
168static int set_pasid_state(struct device_state *dev_state,
169 struct pasid_state *pasid_state,
170 int pasid)
171{
172 struct pasid_state **ptr;
173 unsigned long flags;
174 int ret;
175
176 spin_lock_irqsave(&dev_state->lock, flags);
177 ptr = __get_pasid_state_ptr(dev_state, pasid, true);
178
179 ret = -ENOMEM;
180 if (ptr == NULL)
181 goto out_unlock;
182
183 ret = -ENOMEM;
184 if (*ptr != NULL)
185 goto out_unlock;
186
187 *ptr = pasid_state;
188
189 ret = 0;
190
191out_unlock:
192 spin_unlock_irqrestore(&dev_state->lock, flags);
193
194 return ret;
195}
196
197static void clear_pasid_state(struct device_state *dev_state, int pasid)
198{
199 struct pasid_state **ptr;
200 unsigned long flags;
201
202 spin_lock_irqsave(&dev_state->lock, flags);
203 ptr = __get_pasid_state_ptr(dev_state, pasid, true);
204
205 if (ptr == NULL)
206 goto out_unlock;
207
208 *ptr = NULL;
209
210out_unlock:
211 spin_unlock_irqrestore(&dev_state->lock, flags);
212}
213
214static struct pasid_state *get_pasid_state(struct device_state *dev_state,
215 int pasid)
216{
217 struct pasid_state **ptr, *ret = NULL;
218 unsigned long flags;
219
220 spin_lock_irqsave(&dev_state->lock, flags);
221 ptr = __get_pasid_state_ptr(dev_state, pasid, false);
222
223 if (ptr == NULL)
224 goto out_unlock;
225
226 ret = *ptr;
227 if (ret)
228 atomic_inc(&ret->count);
229
230out_unlock:
231 spin_unlock_irqrestore(&dev_state->lock, flags);
232
233 return ret;
234}
235
236static void free_pasid_state(struct pasid_state *pasid_state)
237{
238 kfree(pasid_state);
239}
240
241static void put_pasid_state(struct pasid_state *pasid_state)
242{
243 if (atomic_dec_and_test(&pasid_state->count)) {
244 put_device_state(pasid_state->device_state);
245 mmput(pasid_state->mm);
246 free_pasid_state(pasid_state);
247 }
248}
249
250static void unbind_pasid(struct device_state *dev_state, int pasid)
251{
252 struct pasid_state *pasid_state;
253
254 pasid_state = get_pasid_state(dev_state, pasid);
255 if (pasid_state == NULL)
256 return;
257
258 unlink_pasid_state(pasid_state);
259
260 amd_iommu_domain_clear_gcr3(dev_state->domain, pasid);
261 clear_pasid_state(dev_state, pasid);
262
263 put_pasid_state(pasid_state); /* Reference taken in this function */
264 put_pasid_state(pasid_state); /* Reference taken in bind() function */
265}
266
267static void free_pasid_states_level1(struct pasid_state **tbl)
268{
269 int i;
270
271 for (i = 0; i < 512; ++i) {
272 if (tbl[i] == NULL)
273 continue;
274
275 free_page((unsigned long)tbl[i]);
276 }
277}
278
279static void free_pasid_states_level2(struct pasid_state **tbl)
280{
281 struct pasid_state **ptr;
282 int i;
283
284 for (i = 0; i < 512; ++i) {
285 if (tbl[i] == NULL)
286 continue;
287
288 ptr = (struct pasid_state **)tbl[i];
289 free_pasid_states_level1(ptr);
290 }
291}
292
293static void free_pasid_states(struct device_state *dev_state)
294{
295 struct pasid_state *pasid_state;
296 int i;
297
298 for (i = 0; i < dev_state->max_pasids; ++i) {
299 pasid_state = get_pasid_state(dev_state, i);
300 if (pasid_state == NULL)
301 continue;
302
303 unbind_pasid(dev_state, i);
304 put_pasid_state(pasid_state);
305 }
306
307 if (dev_state->pasid_levels == 2)
308 free_pasid_states_level2(dev_state->states);
309 else if (dev_state->pasid_levels == 1)
310 free_pasid_states_level1(dev_state->states);
311 else if (dev_state->pasid_levels != 0)
312 BUG();
313
314 free_page((unsigned long)dev_state->states);
315}
316
317int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
318 struct task_struct *task)
319{
320 struct pasid_state *pasid_state;
321 struct device_state *dev_state;
322 u16 devid;
323 int ret;
324
325 might_sleep();
326
327 if (!amd_iommu_v2_supported())
328 return -ENODEV;
329
330 devid = device_id(pdev);
331 dev_state = get_device_state(devid);
332
333 if (dev_state == NULL)
334 return -EINVAL;
335
336 ret = -EINVAL;
337 if (pasid < 0 || pasid >= dev_state->max_pasids)
338 goto out;
339
340 ret = -ENOMEM;
341 pasid_state = kzalloc(sizeof(*pasid_state), GFP_KERNEL);
342 if (pasid_state == NULL)
343 goto out;
344
345 atomic_set(&pasid_state->count, 1);
346 pasid_state->task = task;
347 pasid_state->mm = get_task_mm(task);
348 pasid_state->device_state = dev_state;
349 pasid_state->pasid = pasid;
350
351 if (pasid_state->mm == NULL)
352 goto out_free;
353
354 ret = set_pasid_state(dev_state, pasid_state, pasid);
355 if (ret)
356 goto out_free;
357
358 ret = amd_iommu_domain_set_gcr3(dev_state->domain, pasid,
359 __pa(pasid_state->mm->pgd));
360 if (ret)
361 goto out_clear_state;
362
363 link_pasid_state(pasid_state);
364
365 return 0;
366
367out_clear_state:
368 clear_pasid_state(dev_state, pasid);
369
370out_free:
371 put_pasid_state(pasid_state);
372
373out:
374 put_device_state(dev_state);
375
376 return ret;
377}
378EXPORT_SYMBOL(amd_iommu_bind_pasid);
379
380void amd_iommu_unbind_pasid(struct pci_dev *pdev, int pasid)
381{
382 struct device_state *dev_state;
383 u16 devid;
384
385 might_sleep();
386
387 if (!amd_iommu_v2_supported())
388 return;
389
390 devid = device_id(pdev);
391 dev_state = get_device_state(devid);
392 if (dev_state == NULL)
393 return;
394
395 if (pasid < 0 || pasid >= dev_state->max_pasids)
396 goto out;
397
398 unbind_pasid(dev_state, pasid);
399
400out:
401 put_device_state(dev_state);
402}
403EXPORT_SYMBOL(amd_iommu_unbind_pasid);
404
102int amd_iommu_init_device(struct pci_dev *pdev, int pasids) 405int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
103{ 406{
104 struct device_state *dev_state; 407 struct device_state *dev_state;
@@ -199,6 +502,9 @@ void amd_iommu_free_device(struct pci_dev *pdev)
199 502
200 spin_unlock_irqrestore(&state_lock, flags); 503 spin_unlock_irqrestore(&state_lock, flags);
201 504
505 /* Get rid of any remaining pasid states */
506 free_pasid_states(dev_state);
507
202 put_device_state(dev_state); 508 put_device_state(dev_state);
203} 509}
204EXPORT_SYMBOL(amd_iommu_free_device); 510EXPORT_SYMBOL(amd_iommu_free_device);