diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2014-07-03 11:51:30 -0400 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2014-07-04 06:35:58 -0400 |
commit | 65d5352f12e16dc9321b1e4f8213793565c124be (patch) | |
tree | 959786bc1c8f4a24a2a259e5362eafd08b596a61 | |
parent | c1931090a22b96b223f2a3b8420076f044da7531 (diff) |
iommu/amd: Use iommu_group_get_for_dev()
The common iommu_group_get_for_dev() allows us to greatly simplify
our group lookup for a new device. Also, since we insert IVRS
aliases into the PCI DMA alias quirks, we should alway come up with
the same results as the existing code.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Cc: Joerg Roedel <joro@8bytes.org>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
-rw-r--r-- | drivers/iommu/amd_iommu.c | 164 | ||||
-rw-r--r-- | drivers/iommu/amd_iommu_types.h | 1 |
2 files changed, 5 insertions, 160 deletions
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 25d7571dfc1c..2e03c17c2499 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c | |||
@@ -46,7 +46,6 @@ | |||
46 | #include "amd_iommu_proto.h" | 46 | #include "amd_iommu_proto.h" |
47 | #include "amd_iommu_types.h" | 47 | #include "amd_iommu_types.h" |
48 | #include "irq_remapping.h" | 48 | #include "irq_remapping.h" |
49 | #include "pci.h" | ||
50 | 49 | ||
51 | #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) | 50 | #define CMD_SET_TYPE(cmd, t) ((cmd)->data[1] |= ((t) << 28)) |
52 | 51 | ||
@@ -133,9 +132,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data) | |||
133 | list_del(&dev_data->dev_data_list); | 132 | list_del(&dev_data->dev_data_list); |
134 | spin_unlock_irqrestore(&dev_data_list_lock, flags); | 133 | spin_unlock_irqrestore(&dev_data_list_lock, flags); |
135 | 134 | ||
136 | if (dev_data->group) | ||
137 | iommu_group_put(dev_data->group); | ||
138 | |||
139 | kfree(dev_data); | 135 | kfree(dev_data); |
140 | } | 136 | } |
141 | 137 | ||
@@ -264,167 +260,17 @@ static bool check_device(struct device *dev) | |||
264 | return true; | 260 | return true; |
265 | } | 261 | } |
266 | 262 | ||
267 | static struct pci_bus *find_hosted_bus(struct pci_bus *bus) | ||
268 | { | ||
269 | while (!bus->self) { | ||
270 | if (!pci_is_root_bus(bus)) | ||
271 | bus = bus->parent; | ||
272 | else | ||
273 | return ERR_PTR(-ENODEV); | ||
274 | } | ||
275 | |||
276 | return bus; | ||
277 | } | ||
278 | |||
279 | #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) | ||
280 | |||
281 | static struct pci_dev *get_isolation_root(struct pci_dev *pdev) | ||
282 | { | ||
283 | struct pci_dev *dma_pdev = pdev; | ||
284 | |||
285 | /* Account for quirked devices */ | ||
286 | swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); | ||
287 | |||
288 | /* | ||
289 | * If it's a multifunction device that does not support our | ||
290 | * required ACS flags, add to the same group as lowest numbered | ||
291 | * function that also does not suport the required ACS flags. | ||
292 | */ | ||
293 | if (dma_pdev->multifunction && | ||
294 | !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) { | ||
295 | u8 i, slot = PCI_SLOT(dma_pdev->devfn); | ||
296 | |||
297 | for (i = 0; i < 8; i++) { | ||
298 | struct pci_dev *tmp; | ||
299 | |||
300 | tmp = pci_get_slot(dma_pdev->bus, PCI_DEVFN(slot, i)); | ||
301 | if (!tmp) | ||
302 | continue; | ||
303 | |||
304 | if (!pci_acs_enabled(tmp, REQ_ACS_FLAGS)) { | ||
305 | swap_pci_ref(&dma_pdev, tmp); | ||
306 | break; | ||
307 | } | ||
308 | pci_dev_put(tmp); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * Devices on the root bus go through the iommu. If that's not us, | ||
314 | * find the next upstream device and test ACS up to the root bus. | ||
315 | * Finding the next device may require skipping virtual buses. | ||
316 | */ | ||
317 | while (!pci_is_root_bus(dma_pdev->bus)) { | ||
318 | struct pci_bus *bus = find_hosted_bus(dma_pdev->bus); | ||
319 | if (IS_ERR(bus)) | ||
320 | break; | ||
321 | |||
322 | if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) | ||
323 | break; | ||
324 | |||
325 | swap_pci_ref(&dma_pdev, pci_dev_get(bus->self)); | ||
326 | } | ||
327 | |||
328 | return dma_pdev; | ||
329 | } | ||
330 | |||
331 | static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) | ||
332 | { | ||
333 | struct iommu_group *group = iommu_group_get(&pdev->dev); | ||
334 | int ret; | ||
335 | |||
336 | if (!group) { | ||
337 | group = iommu_group_alloc(); | ||
338 | if (IS_ERR(group)) | ||
339 | return PTR_ERR(group); | ||
340 | |||
341 | WARN_ON(&pdev->dev != dev); | ||
342 | } | ||
343 | |||
344 | ret = iommu_group_add_device(group, dev); | ||
345 | iommu_group_put(group); | ||
346 | return ret; | ||
347 | } | ||
348 | |||
349 | static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, | ||
350 | struct device *dev) | ||
351 | { | ||
352 | if (!dev_data->group) { | ||
353 | struct iommu_group *group = iommu_group_alloc(); | ||
354 | if (IS_ERR(group)) | ||
355 | return PTR_ERR(group); | ||
356 | |||
357 | dev_data->group = group; | ||
358 | } | ||
359 | |||
360 | return iommu_group_add_device(dev_data->group, dev); | ||
361 | } | ||
362 | |||
363 | static int init_iommu_group(struct device *dev) | 263 | static int init_iommu_group(struct device *dev) |
364 | { | 264 | { |
365 | struct iommu_dev_data *dev_data; | ||
366 | struct iommu_group *group; | 265 | struct iommu_group *group; |
367 | struct pci_dev *dma_pdev; | ||
368 | int ret; | ||
369 | 266 | ||
370 | group = iommu_group_get(dev); | 267 | group = iommu_group_get_for_dev(dev); |
371 | if (group) { | ||
372 | iommu_group_put(group); | ||
373 | return 0; | ||
374 | } | ||
375 | 268 | ||
376 | dev_data = find_dev_data(get_device_id(dev)); | 269 | if (IS_ERR(group)) |
377 | if (!dev_data) | 270 | return PTR_ERR(group); |
378 | return -ENOMEM; | ||
379 | |||
380 | if (dev_data->alias_data) { | ||
381 | u16 alias; | ||
382 | struct pci_bus *bus; | ||
383 | |||
384 | if (dev_data->alias_data->group) | ||
385 | goto use_group; | ||
386 | 271 | ||
387 | /* | 272 | iommu_group_put(group); |
388 | * If the alias device exists, it's effectively just a first | 273 | return 0; |
389 | * level quirk for finding the DMA source. | ||
390 | */ | ||
391 | alias = amd_iommu_alias_table[dev_data->devid]; | ||
392 | dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); | ||
393 | if (dma_pdev) { | ||
394 | dma_pdev = get_isolation_root(dma_pdev); | ||
395 | goto use_pdev; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * If the alias is virtual, try to find a parent device | ||
400 | * and test whether the IOMMU group is actualy rooted above | ||
401 | * the alias. Be careful to also test the parent device if | ||
402 | * we think the alias is the root of the group. | ||
403 | */ | ||
404 | bus = pci_find_bus(0, alias >> 8); | ||
405 | if (!bus) | ||
406 | goto use_group; | ||
407 | |||
408 | bus = find_hosted_bus(bus); | ||
409 | if (IS_ERR(bus) || !bus->self) | ||
410 | goto use_group; | ||
411 | |||
412 | dma_pdev = get_isolation_root(pci_dev_get(bus->self)); | ||
413 | if (dma_pdev != bus->self || (dma_pdev->multifunction && | ||
414 | !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) | ||
415 | goto use_pdev; | ||
416 | |||
417 | pci_dev_put(dma_pdev); | ||
418 | goto use_group; | ||
419 | } | ||
420 | |||
421 | dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); | ||
422 | use_pdev: | ||
423 | ret = use_pdev_iommu_group(dma_pdev, dev); | ||
424 | pci_dev_put(dma_pdev); | ||
425 | return ret; | ||
426 | use_group: | ||
427 | return use_dev_data_iommu_group(dev_data->alias_data, dev); | ||
428 | } | 274 | } |
429 | 275 | ||
430 | static int __last_alias(struct pci_dev *pdev, u16 alias, void *data) | 276 | static int __last_alias(struct pci_dev *pdev, u16 alias, void *data) |
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index f1a5abf11acf..7277a200d916 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h | |||
@@ -432,7 +432,6 @@ struct iommu_dev_data { | |||
432 | struct iommu_dev_data *alias_data;/* The alias dev_data */ | 432 | struct iommu_dev_data *alias_data;/* The alias dev_data */ |
433 | struct protection_domain *domain; /* Domain the device is bound to */ | 433 | struct protection_domain *domain; /* Domain the device is bound to */ |
434 | atomic_t bind; /* Domain attach reference count */ | 434 | atomic_t bind; /* Domain attach reference count */ |
435 | struct iommu_group *group; /* IOMMU group for virtual aliases */ | ||
436 | u16 devid; /* PCI Device ID */ | 435 | u16 devid; /* PCI Device ID */ |
437 | bool iommu_v2; /* Device can make use of IOMMUv2 */ | 436 | bool iommu_v2; /* Device can make use of IOMMUv2 */ |
438 | bool passthrough; /* Default for device is pt_domain */ | 437 | bool passthrough; /* Default for device is pt_domain */ |