|
@@ -133,6 +133,104 @@ static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf)
|
|
|
return sprintf(buf, "%s\n", group->name);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * iommu_insert_resv_region - Insert a new region in the
|
|
|
+ * list of reserved regions.
|
|
|
+ * @new: new region to insert
|
|
|
+ * @regions: list of regions
|
|
|
+ *
|
|
|
+ * The new element is sorted by address with respect to the other
|
|
|
+ * regions of the same type. In case it overlaps with another
|
|
|
+ * region of the same type, regions are merged. In case it
|
|
|
+ * overlaps with another region of different type, regions are
|
|
|
+ * not merged.
|
|
|
+ */
|
|
|
+static int iommu_insert_resv_region(struct iommu_resv_region *new,
|
|
|
+ struct list_head *regions)
|
|
|
+{
|
|
|
+ struct iommu_resv_region *region;
|
|
|
+ phys_addr_t start = new->start;
|
|
|
+ phys_addr_t end = new->start + new->length - 1;
|
|
|
+ struct list_head *pos = regions->next;
|
|
|
+
|
|
|
+ while (pos != regions) {
|
|
|
+ struct iommu_resv_region *entry =
|
|
|
+ list_entry(pos, struct iommu_resv_region, list);
|
|
|
+ phys_addr_t a = entry->start;
|
|
|
+ phys_addr_t b = entry->start + entry->length - 1;
|
|
|
+ int type = entry->type;
|
|
|
+
|
|
|
+ if (end < a) {
|
|
|
+ goto insert;
|
|
|
+ } else if (start > b) {
|
|
|
+ pos = pos->next;
|
|
|
+ } else if ((start >= a) && (end <= b)) {
|
|
|
+ if (new->type == type)
|
|
|
+ goto done;
|
|
|
+ else
|
|
|
+ pos = pos->next;
|
|
|
+ } else {
|
|
|
+ if (new->type == type) {
|
|
|
+ phys_addr_t new_start = min(a, start);
|
|
|
+ phys_addr_t new_end = max(b, end);
|
|
|
+
|
|
|
+ list_del(&entry->list);
|
|
|
+ entry->start = new_start;
|
|
|
+ entry->length = new_end - new_start + 1;
|
|
|
+ iommu_insert_resv_region(entry, regions);
|
|
|
+ } else {
|
|
|
+ pos = pos->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+insert:
|
|
|
+ region = iommu_alloc_resv_region(new->start, new->length,
|
|
|
+ new->prot, new->type);
|
|
|
+ if (!region)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ list_add_tail(®ion->list, pos);
|
|
|
+done:
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+iommu_insert_device_resv_regions(struct list_head *dev_resv_regions,
|
|
|
+ struct list_head *group_resv_regions)
|
|
|
+{
|
|
|
+ struct iommu_resv_region *entry;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ list_for_each_entry(entry, dev_resv_regions, list) {
|
|
|
+ ret = iommu_insert_resv_region(entry, group_resv_regions);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+int iommu_get_group_resv_regions(struct iommu_group *group,
|
|
|
+ struct list_head *head)
|
|
|
+{
|
|
|
+ struct iommu_device *device;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&group->mutex);
|
|
|
+ list_for_each_entry(device, &group->devices, list) {
|
|
|
+ struct list_head dev_resv_regions;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&dev_resv_regions);
|
|
|
+ iommu_get_resv_regions(device->dev, &dev_resv_regions);
|
|
|
+ ret = iommu_insert_device_resv_regions(&dev_resv_regions, head);
|
|
|
+ iommu_put_resv_regions(device->dev, &dev_resv_regions);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mutex_unlock(&group->mutex);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(iommu_get_group_resv_regions);
|
|
|
+
|
|
|
static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL);
|
|
|
|
|
|
static void iommu_group_release(struct kobject *kobj)
|