Skip to content

Commit bf8d2dd

Browse files
niklas88joergroedel
authored andcommitted
iommu/s390: Fix duplicate domain attachments
Since commit fa7e9ec ("iommu/s390: Tolerate repeat attach_dev calls") we can end up with duplicates in the list of devices attached to a domain. This is inefficient and confusing since only one domain can actually be in control of the IOMMU translations for a device. Fix this by detaching the device from the previous domain, if any, on attach. Add a WARN_ON() in case we still have attached devices on freeing the domain. While here remove the re-attach on failure dance as it was determined to be unlikely to help and may confuse debug and recovery. Fixes: fa7e9ec ("iommu/s390: Tolerate repeat attach_dev calls") Reviewed-by: Matthew Rosato <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Signed-off-by: Niklas Schnelle <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Joerg Roedel <[email protected]>
1 parent 30a0b95 commit bf8d2dd

File tree

1 file changed

+45
-61
lines changed

1 file changed

+45
-61
lines changed

drivers/iommu/s390-iommu.c

Lines changed: 45 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,44 @@ static void s390_domain_free(struct iommu_domain *domain)
7979
{
8080
struct s390_domain *s390_domain = to_s390_domain(domain);
8181

82+
WARN_ON(!list_empty(&s390_domain->devices));
8283
dma_cleanup_tables(s390_domain->dma_table);
8384
kfree(s390_domain);
8485
}
8586

87+
static void __s390_iommu_detach_device(struct zpci_dev *zdev)
88+
{
89+
struct s390_domain *s390_domain = zdev->s390_domain;
90+
struct s390_domain_device *domain_device, *tmp;
91+
unsigned long flags;
92+
93+
if (!s390_domain)
94+
return;
95+
96+
spin_lock_irqsave(&s390_domain->list_lock, flags);
97+
list_for_each_entry_safe(domain_device, tmp, &s390_domain->devices,
98+
list) {
99+
if (domain_device->zdev == zdev) {
100+
list_del(&domain_device->list);
101+
kfree(domain_device);
102+
break;
103+
}
104+
}
105+
spin_unlock_irqrestore(&s390_domain->list_lock, flags);
106+
107+
zpci_unregister_ioat(zdev, 0);
108+
zdev->s390_domain = NULL;
109+
zdev->dma_table = NULL;
110+
}
111+
86112
static int s390_iommu_attach_device(struct iommu_domain *domain,
87113
struct device *dev)
88114
{
89115
struct s390_domain *s390_domain = to_s390_domain(domain);
90116
struct zpci_dev *zdev = to_zpci_dev(dev);
91117
struct s390_domain_device *domain_device;
92118
unsigned long flags;
93-
int cc, rc;
119+
int cc, rc = 0;
94120

95121
if (!zdev)
96122
return -ENODEV;
@@ -99,24 +125,18 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
99125
if (!domain_device)
100126
return -ENOMEM;
101127

102-
if (zdev->dma_table && !zdev->s390_domain) {
103-
cc = zpci_dma_exit_device(zdev);
104-
if (cc) {
105-
rc = -EIO;
106-
goto out_free;
107-
}
108-
}
109-
110128
if (zdev->s390_domain)
111-
zpci_unregister_ioat(zdev, 0);
129+
__s390_iommu_detach_device(zdev);
130+
else if (zdev->dma_table)
131+
zpci_dma_exit_device(zdev);
112132

113-
zdev->dma_table = s390_domain->dma_table;
114133
cc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
115-
virt_to_phys(zdev->dma_table));
134+
virt_to_phys(s390_domain->dma_table));
116135
if (cc) {
117136
rc = -EIO;
118-
goto out_restore;
137+
goto out_free;
119138
}
139+
zdev->dma_table = s390_domain->dma_table;
120140

121141
spin_lock_irqsave(&s390_domain->list_lock, flags);
122142
/* First device defines the DMA range limits */
@@ -127,9 +147,9 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
127147
/* Allow only devices with identical DMA range limits */
128148
} else if (domain->geometry.aperture_start != zdev->start_dma ||
129149
domain->geometry.aperture_end != zdev->end_dma) {
130-
rc = -EINVAL;
131150
spin_unlock_irqrestore(&s390_domain->list_lock, flags);
132-
goto out_restore;
151+
rc = -EINVAL;
152+
goto out_unregister;
133153
}
134154
domain_device->zdev = zdev;
135155
zdev->s390_domain = s390_domain;
@@ -138,14 +158,9 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
138158

139159
return 0;
140160

141-
out_restore:
142-
if (!zdev->s390_domain) {
143-
zpci_dma_init_device(zdev);
144-
} else {
145-
zdev->dma_table = zdev->s390_domain->dma_table;
146-
zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma,
147-
virt_to_phys(zdev->dma_table));
148-
}
161+
out_unregister:
162+
zpci_unregister_ioat(zdev, 0);
163+
zdev->dma_table = NULL;
149164
out_free:
150165
kfree(domain_device);
151166

@@ -155,32 +170,12 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
155170
static void s390_iommu_detach_device(struct iommu_domain *domain,
156171
struct device *dev)
157172
{
158-
struct s390_domain *s390_domain = to_s390_domain(domain);
159173
struct zpci_dev *zdev = to_zpci_dev(dev);
160-
struct s390_domain_device *domain_device, *tmp;
161-
unsigned long flags;
162-
int found = 0;
163174

164-
if (!zdev)
165-
return;
175+
WARN_ON(zdev->s390_domain != to_s390_domain(domain));
166176

167-
spin_lock_irqsave(&s390_domain->list_lock, flags);
168-
list_for_each_entry_safe(domain_device, tmp, &s390_domain->devices,
169-
list) {
170-
if (domain_device->zdev == zdev) {
171-
list_del(&domain_device->list);
172-
kfree(domain_device);
173-
found = 1;
174-
break;
175-
}
176-
}
177-
spin_unlock_irqrestore(&s390_domain->list_lock, flags);
178-
179-
if (found && (zdev->s390_domain == s390_domain)) {
180-
zdev->s390_domain = NULL;
181-
zpci_unregister_ioat(zdev, 0);
182-
zpci_dma_init_device(zdev);
183-
}
177+
__s390_iommu_detach_device(zdev);
178+
zpci_dma_init_device(zdev);
184179
}
185180

186181
static struct iommu_device *s390_iommu_probe_device(struct device *dev)
@@ -198,24 +193,13 @@ static struct iommu_device *s390_iommu_probe_device(struct device *dev)
198193
static void s390_iommu_release_device(struct device *dev)
199194
{
200195
struct zpci_dev *zdev = to_zpci_dev(dev);
201-
struct iommu_domain *domain;
202196

203197
/*
204-
* This is a workaround for a scenario where the IOMMU API common code
205-
* "forgets" to call the detach_dev callback: After binding a device
206-
* to vfio-pci and completing the VFIO_SET_IOMMU ioctl (which triggers
207-
* the attach_dev), removing the device via
208-
* "echo 1 > /sys/bus/pci/devices/.../remove" won't trigger detach_dev,
209-
* only release_device will be called via the BUS_NOTIFY_REMOVED_DEVICE
210-
* notifier.
211-
*
212-
* So let's call detach_dev from here if it hasn't been called before.
198+
* release_device is expected to detach any domain currently attached
199+
* to the device, but keep it attached to other devices in the group.
213200
*/
214-
if (zdev && zdev->s390_domain) {
215-
domain = iommu_get_domain_for_dev(dev);
216-
if (domain)
217-
s390_iommu_detach_device(domain, dev);
218-
}
201+
if (zdev)
202+
__s390_iommu_detach_device(zdev);
219203
}
220204

221205
static int s390_iommu_update_trans(struct s390_domain *s390_domain,

0 commit comments

Comments
 (0)