diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2009-11-25 09:59:57 -0500 |
---|---|---|
committer | Joerg Roedel <joerg.roedel@amd.com> | 2009-11-27 08:20:33 -0500 |
commit | 241000556f751dacd332df6ab2e903a23746e51e (patch) | |
tree | b3dad45997c5e3c9eb754901e24dbca9c959852a | |
parent | 657cbb6b6cba0f9c98c5299e0c803b2c0e67ea0a (diff) |
x86/amd-iommu: Add device bind reference counting
This patch adds a reference count to each device to count
how often the device was bound to that domain. This is
important for single devices that act as an alias for a
number of others. These devices must stay bound to their
domains until all devices that alias to it are unbound from
the same domain.
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
-rw-r--r-- | arch/x86/include/asm/amd_iommu_types.h | 1 | ||||
-rw-r--r-- | arch/x86/kernel/amd_iommu.c | 37 |
2 files changed, 30 insertions, 8 deletions
diff --git a/arch/x86/include/asm/amd_iommu_types.h b/arch/x86/include/asm/amd_iommu_types.h index 9eaa27b46860..434e90ed89c5 100644 --- a/arch/x86/include/asm/amd_iommu_types.h +++ b/arch/x86/include/asm/amd_iommu_types.h | |||
@@ -253,6 +253,7 @@ struct protection_domain { | |||
253 | struct iommu_dev_data { | 253 | struct iommu_dev_data { |
254 | struct device *alias; /* The Alias Device */ | 254 | struct device *alias; /* The Alias Device */ |
255 | struct protection_domain *domain; /* Domain the device is bound to */ | 255 | struct protection_domain *domain; /* Domain the device is bound to */ |
256 | atomic_t bind; /* Domain attach reverent count */ | ||
256 | }; | 257 | }; |
257 | 258 | ||
258 | /* | 259 | /* |
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c index 3214e8806f95..f5db7d5e444e 100644 --- a/arch/x86/kernel/amd_iommu.c +++ b/arch/x86/kernel/amd_iommu.c | |||
@@ -152,6 +152,8 @@ static int iommu_init_device(struct device *dev) | |||
152 | if (pdev) | 152 | if (pdev) |
153 | dev_data->alias = &pdev->dev; | 153 | dev_data->alias = &pdev->dev; |
154 | 154 | ||
155 | atomic_set(&dev_data->bind, 0); | ||
156 | |||
155 | dev->archdata.iommu = dev_data; | 157 | dev->archdata.iommu = dev_data; |
156 | 158 | ||
157 | 159 | ||
@@ -1403,10 +1405,13 @@ static int __attach_device(struct device *dev, | |||
1403 | return -EBUSY; | 1405 | return -EBUSY; |
1404 | 1406 | ||
1405 | /* Do real assignment */ | 1407 | /* Do real assignment */ |
1406 | if (alias != devid && | 1408 | if (alias != devid) { |
1407 | alias_data->domain == NULL) { | 1409 | if (alias_data->domain == NULL) { |
1408 | alias_data->domain = domain; | 1410 | alias_data->domain = domain; |
1409 | set_dte_entry(alias, domain); | 1411 | set_dte_entry(alias, domain); |
1412 | } | ||
1413 | |||
1414 | atomic_inc(&alias_data->bind); | ||
1410 | } | 1415 | } |
1411 | 1416 | ||
1412 | if (dev_data->domain == NULL) { | 1417 | if (dev_data->domain == NULL) { |
@@ -1414,6 +1419,8 @@ static int __attach_device(struct device *dev, | |||
1414 | set_dte_entry(devid, domain); | 1419 | set_dte_entry(devid, domain); |
1415 | } | 1420 | } |
1416 | 1421 | ||
1422 | atomic_inc(&dev_data->bind); | ||
1423 | |||
1417 | /* ready */ | 1424 | /* ready */ |
1418 | spin_unlock(&domain->lock); | 1425 | spin_unlock(&domain->lock); |
1419 | 1426 | ||
@@ -1449,20 +1456,34 @@ static int attach_device(struct device *dev, | |||
1449 | */ | 1456 | */ |
1450 | static void __detach_device(struct device *dev) | 1457 | static void __detach_device(struct device *dev) |
1451 | { | 1458 | { |
1452 | u16 devid = get_device_id(dev); | 1459 | u16 devid = get_device_id(dev), alias; |
1453 | struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; | 1460 | struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; |
1454 | struct iommu_dev_data *dev_data = get_dev_data(dev); | 1461 | struct iommu_dev_data *dev_data = get_dev_data(dev); |
1462 | struct iommu_dev_data *alias_data; | ||
1455 | 1463 | ||
1456 | BUG_ON(!iommu); | 1464 | BUG_ON(!iommu); |
1457 | 1465 | ||
1458 | clear_dte_entry(devid); | 1466 | devid = get_device_id(dev); |
1459 | dev_data->domain = NULL; | 1467 | alias = get_device_id(dev_data->alias); |
1468 | |||
1469 | if (devid != alias) { | ||
1470 | alias_data = get_dev_data(dev_data->alias); | ||
1471 | if (atomic_dec_and_test(&alias_data->bind)) { | ||
1472 | clear_dte_entry(alias); | ||
1473 | alias_data->domain = NULL; | ||
1474 | } | ||
1475 | } | ||
1476 | |||
1477 | if (atomic_dec_and_test(&dev_data->bind)) { | ||
1478 | clear_dte_entry(devid); | ||
1479 | dev_data->domain = NULL; | ||
1480 | } | ||
1460 | 1481 | ||
1461 | /* | 1482 | /* |
1462 | * If we run in passthrough mode the device must be assigned to the | 1483 | * If we run in passthrough mode the device must be assigned to the |
1463 | * passthrough domain if it is detached from any other domain | 1484 | * passthrough domain if it is detached from any other domain |
1464 | */ | 1485 | */ |
1465 | if (iommu_pass_through) | 1486 | if (iommu_pass_through && dev_data->domain == NULL) |
1466 | __attach_device(dev, pt_domain); | 1487 | __attach_device(dev, pt_domain); |
1467 | } | 1488 | } |
1468 | 1489 | ||