| @@ -62,6 +62,7 @@ MODULE_PARM_DESC(dma_entry_limit, · 61 unmodified lines |
| 62 | 62 | |
| 63 | 63 | struct vfio_iommu { |
| 64 | 64 | struct list_head domain_list; |
| 65 | + struct list_head iova_list; |
| 65 | 66 | struct vfio_domain *external_domain; /* domain for external user */ |
| 66 | 67 | struct mutex lock; |
| 67 | 68 | struct rb_root dma_list; |
| @@ -97,6 +98,12 @@ struct vfio_group { · 29 unmodified lines |
| 97 | 98 | bool mdev_group; /* An mdev group */ |
| 98 | 99 | }; |
| 99 | 100 | |
| 101 | +struct vfio_iova { |
| 102 | + struct list_head list; |
| 103 | + dma_addr_t start; |
| 104 | + dma_addr_t end; |
| 105 | +}; |
| 106 | + |
| 100 | 107 | /* |
| 101 | 108 | * Guest RAM pinning working set or DMA target |
| 102 | 109 | */ |
| @@ -1031,6 +1038,27 @@ static int vfio_pin_map_dma(struct vfio_iommu *iommu, struct vfio_dma *dma, · 928 unmodified lines |
| 1031 | 1038 | return ret; |
| 1032 | 1039 | } |
| 1033 | 1040 | |
| 1041 | +/* |
| 1042 | + * Check dma map request is within a valid iova range |
| 1043 | + */ |
| 1044 | +static bool vfio_iommu_iova_dma_valid(struct vfio_iommu *iommu, |
| 1045 | + dma_addr_t start, dma_addr_t end) |
| 1046 | +{ |
| 1047 | + struct list_head *iova = &iommu->iova_list; |
| 1048 | + struct vfio_iova *node; |
| 1049 | + |
| 1050 | + list_for_each_entry(node, iova, list) { |
| 1051 | + if (start >= node->start && end <= node->end) |
| 1052 | + return true; |
| 1053 | + } |
| 1054 | + |
| 1055 | + /* |
| 1056 | + * Check for list_empty() as well since a container with |
| 1057 | + * a single mdev device will have an empty list. |
| 1058 | + */ |
| 1059 | + return list_empty(iova); |
| 1060 | +} |
| 1061 | + |
| 1034 | 1062 | static int vfio_dma_do_map(struct vfio_iommu *iommu, |
| 1035 | 1063 | struct vfio_iommu_type1_dma_map *map) |
| 1036 | 1064 | { |
| @@ -1074,6 +1102,11 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, · 37 unmodified lines |
| 1074 | 1102 | goto out_unlock; |
| 1075 | 1103 | } |
| 1076 | 1104 | |
| 1105 | + if (!vfio_iommu_iova_dma_valid(iommu, iova, iova + size - 1)) { |
| 1106 | + ret = -EINVAL; |
| 1107 | + goto out_unlock; |
| 1108 | + } |
| 1109 | + |
| 1077 | 1110 | dma = kzalloc(sizeof(*dma), GFP_KERNEL); |
| 1078 | 1111 | if (!dma) { |
| 1079 | 1112 | ret = -ENOMEM; |
| @@ -1263,15 +1296,13 @@ static struct vfio_group *find_iommu_group(struct vfio_domain *domain, · 183 unmodified lines |
| 1263 | 1296 | return NULL; |
| 1264 | 1297 | } |
| 1265 | 1298 | |
| 1266 | | −static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base) |
| 1299 | +static bool vfio_iommu_has_sw_msi(struct list_head *group_resv_regions, |
| 1300 | + phys_addr_t *base) |
| 1267 | 1301 | { |
| 1268 | | − struct list_head group_resv_regions; |
| 1269 | | − struct iommu_resv_region *region, *next; |
| 1302 | + struct iommu_resv_region *region; |
| 1270 | 1303 | bool ret = false; |
| 1271 | 1304 | |
| 1272 | | − INIT_LIST_HEAD(&group_resv_regions); |
| 1273 | | − iommu_get_group_resv_regions(group, &group_resv_regions); |
| 1274 | | − list_for_each_entry(region, &group_resv_regions, list) { |
| 1305 | + list_for_each_entry(region, group_resv_regions, list) { |
| 1275 | 1306 | /* |
| 1276 | 1307 | * The presence of any 'real' MSI regions should take |
| 1277 | 1308 | * precedence over the software-managed one if the |
| @@ -1287,8 +1318,7 @@ static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base) · 9 unmodified lines |
| 1287 | 1318 | ret = true; |
| 1288 | 1319 | } |
| 1289 | 1320 | } |
| 1290 | | − list_for_each_entry_safe(region, next, &group_resv_regions, list) |
| 1291 | | − kfree(region); |
| 1321 | + |
| 1292 | 1322 | return ret; |
| 1293 | 1323 | } |
| 1294 | 1324 | |
| @@ -1388,6 +1418,228 @@ static int vfio_mdev_iommu_device(struct device *dev, void *data) · 93 unmodified lines |
| 1388 | 1418 | return 0; |
| 1389 | 1419 | } |
| 1390 | 1420 | |
| 1421 | +/* |
| 1422 | + * This is a helper function to insert an address range to iova list. |
| 1423 | + * The list is initially created with a single entry corresponding to |
| 1424 | + * the IOMMU domain geometry to which the device group is attached. |
| 1425 | + * The list aperture gets modified when a new domain is added to the |
| 1426 | + * container if the new aperture doesn't conflict with the current one |
| 1427 | + * or with any existing dma mappings. The list is also modified to |
| 1428 | + * exclude any reserved regions associated with the device group. |
| 1429 | + */ |
| 1430 | +static int vfio_iommu_iova_insert(struct list_head *head, |
| 1431 | + dma_addr_t start, dma_addr_t end) |
| 1432 | +{ |
| 1433 | + struct vfio_iova *region; |
| 1434 | + |
| 1435 | + region = kmalloc(sizeof(*region), GFP_KERNEL); |
| 1436 | + if (!region) |
| 1437 | + return -ENOMEM; |
| 1438 | + |
| 1439 | + INIT_LIST_HEAD(®ion->list); |
| 1440 | + region->start = start; |
| 1441 | + region->end = end; |
| 1442 | + |
| 1443 | + list_add_tail(®ion->list, head); |
| 1444 | + return 0; |
| 1445 | +} |
| 1446 | + |
| 1447 | +/* |
| 1448 | + * Check the new iommu aperture conflicts with existing aper or with any |
| 1449 | + * existing dma mappings. |
| 1450 | + */ |
| 1451 | +static bool vfio_iommu_aper_conflict(struct vfio_iommu *iommu, |
| 1452 | + dma_addr_t start, dma_addr_t end) |
| 1453 | +{ |
| 1454 | + struct vfio_iova *first, *last; |
| 1455 | + struct list_head *iova = &iommu->iova_list; |
| 1456 | + |
| 1457 | + if (list_empty(iova)) |
| 1458 | + return false; |
| 1459 | + |
| 1460 | + /* Disjoint sets, return conflict */ |
| 1461 | + first = list_first_entry(iova, struct vfio_iova, list); |
| 1462 | + last = list_last_entry(iova, struct vfio_iova, list); |
| 1463 | + if (start > last->end || end < first->start) |
| 1464 | + return true; |
| 1465 | + |
| 1466 | + /* Check for any existing dma mappings below the new start */ |
| 1467 | + if (start > first->start) { |
| 1468 | + if (vfio_find_dma(iommu, first->start, start - first->start)) |
| 1469 | + return true; |
| 1470 | + } |
| 1471 | + |
| 1472 | + /* Check for any existing dma mappings beyond the new end */ |
| 1473 | + if (end < last->end) { |
| 1474 | + if (vfio_find_dma(iommu, end + 1, last->end - end)) |
| 1475 | + return true; |
| 1476 | + } |
| 1477 | + |
| 1478 | + return false; |
| 1479 | +} |
| 1480 | + |
| 1481 | +/* |
| 1482 | + * Resize iommu iova aperture window. This is called only if the new |
| 1483 | + * aperture has no conflict with existing aperture and dma mappings. |
| 1484 | + */ |
| 1485 | +static int vfio_iommu_aper_resize(struct list_head *iova, |
| 1486 | + dma_addr_t start, dma_addr_t end) |
| 1487 | +{ |
| 1488 | + struct vfio_iova *node, *next; |
| 1489 | + |
| 1490 | + if (list_empty(iova)) |
| 1491 | + return vfio_iommu_iova_insert(iova, start, end); |
| 1492 | + |
| 1493 | + /* Adjust iova list start */ |
| 1494 | + list_for_each_entry_safe(node, next, iova, list) { |
| 1495 | + if (start < node->start) |
| 1496 | + break; |
| 1497 | + if (start >= node->start && start < node->end) { |
| 1498 | + node->start = start; |
| 1499 | + break; |
| 1500 | + } |
| 1501 | + /* Delete nodes before new start */ |
| 1502 | + list_del(&node->list); |
| 1503 | + kfree(node); |
| 1504 | + } |
| 1505 | + |
| 1506 | + /* Adjust iova list end */ |
| 1507 | + list_for_each_entry_safe(node, next, iova, list) { |
| 1508 | + if (end > node->end) |
| 1509 | + continue; |
| 1510 | + if (end > node->start && end <= node->end) { |
| 1511 | + node->end = end; |
| 1512 | + continue; |
| 1513 | + } |
| 1514 | + /* Delete nodes after new end */ |
| 1515 | + list_del(&node->list); |
| 1516 | + kfree(node); |
| 1517 | + } |
| 1518 | + |
| 1519 | + return 0; |
| 1520 | +} |
| 1521 | + |
| 1522 | +/* |
| 1523 | + * Check reserved region conflicts with existing dma mappings |
| 1524 | + */ |
| 1525 | +static bool vfio_iommu_resv_conflict(struct vfio_iommu *iommu, |
| 1526 | + struct list_head *resv_regions) |
| 1527 | +{ |
| 1528 | + struct iommu_resv_region *region; |
| 1529 | + |
| 1530 | + /* Check for conflict with existing dma mappings */ |
| 1531 | + list_for_each_entry(region, resv_regions, list) { |
| 1532 | + if (region->type == IOMMU_RESV_DIRECT_RELAXABLE) |
| 1533 | + continue; |
| 1534 | + |
| 1535 | + if (vfio_find_dma(iommu, region->start, region->length)) |
| 1536 | + return true; |
| 1537 | + } |
| 1538 | + |
| 1539 | + return false; |
| 1540 | +} |
| 1541 | + |
| 1542 | +/* |
| 1543 | + * Check iova region overlap with reserved regions and |
| 1544 | + * exclude them from the iommu iova range |
| 1545 | + */ |
| 1546 | +static int vfio_iommu_resv_exclude(struct list_head *iova, |
| 1547 | + struct list_head *resv_regions) |
| 1548 | +{ |
| 1549 | + struct iommu_resv_region *resv; |
| 1550 | + struct vfio_iova *n, *next; |
| 1551 | + |
| 1552 | + list_for_each_entry(resv, resv_regions, list) { |
| 1553 | + phys_addr_t start, end; |
| 1554 | + |
| 1555 | + if (resv->type == IOMMU_RESV_DIRECT_RELAXABLE) |
| 1556 | + continue; |
| 1557 | + |
| 1558 | + start = resv->start; |
| 1559 | + end = resv->start + resv->length - 1; |
| 1560 | + |
| 1561 | + list_for_each_entry_safe(n, next, iova, list) { |
| 1562 | + int ret = 0; |
| 1563 | + |
| 1564 | + /* No overlap */ |
| 1565 | + if (start > n->end || end < n->start) |
| 1566 | + continue; |
| 1567 | + /* |
| 1568 | + * Insert a new node if current node overlaps with the |
| 1569 | + * reserve region to exlude that from valid iova range. |
| 1570 | + * Note that, new node is inserted before the current |
| 1571 | + * node and finally the current node is deleted keeping |
| 1572 | + * the list updated and sorted. |
| 1573 | + */ |
| 1574 | + if (start > n->start) |
| 1575 | + ret = vfio_iommu_iova_insert(&n->list, n->start, |
| 1576 | + start - 1); |
| 1577 | + if (!ret && end < n->end) |
| 1578 | + ret = vfio_iommu_iova_insert(&n->list, end + 1, |
| 1579 | + n->end); |
| 1580 | + if (ret) |
| 1581 | + return ret; |
| 1582 | + |
| 1583 | + list_del(&n->list); |
| 1584 | + kfree(n); |
| 1585 | + } |
| 1586 | + } |
| 1587 | + |
| 1588 | + if (list_empty(iova)) |
| 1589 | + return -EINVAL; |
| 1590 | + |
| 1591 | + return 0; |
| 1592 | +} |
| 1593 | + |
| 1594 | +static void vfio_iommu_resv_free(struct list_head *resv_regions) |
| 1595 | +{ |
| 1596 | + struct iommu_resv_region *n, *next; |
| 1597 | + |
| 1598 | + list_for_each_entry_safe(n, next, resv_regions, list) { |
| 1599 | + list_del(&n->list); |
| 1600 | + kfree(n); |
| 1601 | + } |
| 1602 | +} |
| 1603 | + |
| 1604 | +static void vfio_iommu_iova_free(struct list_head *iova) |
| 1605 | +{ |
| 1606 | + struct vfio_iova *n, *next; |
| 1607 | + |
| 1608 | + list_for_each_entry_safe(n, next, iova, list) { |
| 1609 | + list_del(&n->list); |
| 1610 | + kfree(n); |
| 1611 | + } |
| 1612 | +} |
| 1613 | + |
| 1614 | +static int vfio_iommu_iova_get_copy(struct vfio_iommu *iommu, |
| 1615 | + struct list_head *iova_copy) |
| 1616 | +{ |
| 1617 | + struct list_head *iova = &iommu->iova_list; |
| 1618 | + struct vfio_iova *n; |
| 1619 | + int ret; |
| 1620 | + |
| 1621 | + list_for_each_entry(n, iova, list) { |
| 1622 | + ret = vfio_iommu_iova_insert(iova_copy, n->start, n->end); |
| 1623 | + if (ret) |
| 1624 | + goto out_free; |
| 1625 | + } |
| 1626 | + |
| 1627 | + return 0; |
| 1628 | + |
| 1629 | +out_free: |
| 1630 | + vfio_iommu_iova_free(iova_copy); |
| 1631 | + return ret; |
| 1632 | +} |
| 1633 | + |
| 1634 | +static void vfio_iommu_iova_insert_copy(struct vfio_iommu *iommu, |
| 1635 | + struct list_head *iova_copy) |
| 1636 | +{ |
| 1637 | + struct list_head *iova = &iommu->iova_list; |
| 1638 | + |
| 1639 | + vfio_iommu_iova_free(iova); |
| 1640 | + |
| 1641 | + list_splice_tail(iova_copy, iova); |
| 1642 | +} |
| 1391 | 1643 | static int vfio_iommu_type1_attach_group(void *iommu_data, |
| 1392 | 1644 | struct iommu_group *iommu_group) |
| 1393 | 1645 | { |
| @@ -1398,6 +1650,9 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, · 4 unmodified lines |
| 1398 | 1650 | int ret; |
| 1399 | 1651 | bool resv_msi, msi_remap; |
| 1400 | 1652 | phys_addr_t resv_msi_base; |
| 1653 | + struct iommu_domain_geometry geo; |
| 1654 | + LIST_HEAD(iova_copy); |
| 1655 | + LIST_HEAD(group_resv_regions); |
| 1401 | 1656 | |
| 1402 | 1657 | mutex_lock(&iommu->lock); |
| 1403 | 1658 | |
| @@ -1474,7 +1729,43 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, · 70 unmodified lines |
| 1474 | 1729 | if (ret) |
| 1475 | 1730 | goto out_domain; |
| 1476 | 1731 | |
| 1477 | | − resv_msi = vfio_iommu_has_sw_msi(iommu_group, &resv_msi_base); |
| 1732 | + /* Get aperture info */ |
| 1733 | + iommu_domain_get_attr(domain->domain, DOMAIN_ATTR_GEOMETRY, &geo); |
| 1734 | + |
| 1735 | + if (vfio_iommu_aper_conflict(iommu, geo.aperture_start, |
| 1736 | + geo.aperture_end)) { |
| 1737 | + ret = -EINVAL; |
| 1738 | + goto out_detach; |
| 1739 | + } |
| 1740 | + |
| 1741 | + ret = iommu_get_group_resv_regions(iommu_group, &group_resv_regions); |
| 1742 | + if (ret) |
| 1743 | + goto out_detach; |
| 1744 | + |
| 1745 | + if (vfio_iommu_resv_conflict(iommu, &group_resv_regions)) { |
| 1746 | + ret = -EINVAL; |
| 1747 | + goto out_detach; |
| 1748 | + } |
| 1749 | + |
| 1750 | + /* |
| 1751 | + * We don't want to work on the original iova list as the list |
| 1752 | + * gets modified and in case of failure we have to retain the |
| 1753 | + * original list. Get a copy here. |
| 1754 | + */ |
| 1755 | + ret = vfio_iommu_iova_get_copy(iommu, &iova_copy); |
| 1756 | + if (ret) |
| 1757 | + goto out_detach; |
| 1758 | + |
| 1759 | + ret = vfio_iommu_aper_resize(&iova_copy, geo.aperture_start, |
| 1760 | + geo.aperture_end); |
| 1761 | + if (ret) |
| 1762 | + goto out_detach; |
| 1763 | + |
| 1764 | + ret = vfio_iommu_resv_exclude(&iova_copy, &group_resv_regions); |
| 1765 | + if (ret) |
| 1766 | + goto out_detach; |
| 1767 | + |
| 1768 | + resv_msi = vfio_iommu_has_sw_msi(&group_resv_regions, &resv_msi_base); |
| 1478 | 1769 | |
| 1479 | 1770 | INIT_LIST_HEAD(&domain->group_list); |
| 1480 | 1771 | list_add(&group->next, &domain->group_list); |
| @@ -1507,8 +1798,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, · 26 unmodified lines |
| 1507 | 1798 | list_add(&group->next, &d->group_list); |
| 1508 | 1799 | iommu_domain_free(domain->domain); |
| 1509 | 1800 | kfree(domain); |
| 1510 | | − mutex_unlock(&iommu->lock); |
| 1511 | | − return 0; |
| 1801 | + goto done; |
| 1512 | 1802 | } |
| 1513 | 1803 | |
| 1514 | 1804 | ret = vfio_iommu_attach_group(domain, group); |
| @@ -1531,8 +1821,11 @@ static int vfio_iommu_type1_attach_group(void *iommu_data, · 16 unmodified lines |
| 1531 | 1821 | } |
| 1532 | 1822 | |
| 1533 | 1823 | list_add(&domain->next, &iommu->domain_list); |
| 1534 | | − |
| 1824 | +done: |
| 1825 | + /* Delete the old one and insert new iova list */ |
| 1826 | + vfio_iommu_iova_insert_copy(iommu, &iova_copy); |
| 1535 | 1827 | mutex_unlock(&iommu->lock); |
| 1828 | + vfio_iommu_resv_free(&group_resv_regions); |
| 1536 | 1829 | |
| 1537 | 1830 | return 0; |
| 1538 | 1831 | |
| @@ -1540,6 +1833,8 @@ out_detach: · 1 unmodified line |
| 1540 | 1833 | vfio_iommu_detach_group(domain, group); |
| 1541 | 1834 | out_domain: |
| 1542 | 1835 | iommu_domain_free(domain->domain); |
| 1836 | + vfio_iommu_iova_free(&iova_copy); |
| 1837 | + vfio_iommu_resv_free(&group_resv_regions); |
| 1543 | 1838 | out_free: |
| 1544 | 1839 | kfree(domain); |
| 1545 | 1840 | kfree(group); |
| @@ -1595,12 +1890,93 @@ static void vfio_sanity_check_pfn_list(struct vfio_iommu *iommu) · 49 unmodified lines |
| 1595 | 1890 | WARN_ON(iommu->notifier.head); |
| 1596 | 1891 | } |
| 1597 | 1892 | |
| 1893 | +/* |
| 1894 | + * Called when a domain is removed in detach. It is possible that |
| 1895 | + * the removed domain decided the iova aperture window. Modify the |
| 1896 | + * iova aperture with the smallest window among existing domains. |
| 1897 | + */ |
| 1898 | +static void vfio_iommu_aper_expand(struct vfio_iommu *iommu, |
| 1899 | + struct list_head *iova_copy) |
| 1900 | +{ |
| 1901 | + struct vfio_domain *domain; |
| 1902 | + struct iommu_domain_geometry geo; |
| 1903 | + struct vfio_iova *node; |
| 1904 | + dma_addr_t start = 0; |
| 1905 | + dma_addr_t end = (dma_addr_t)~0; |
| 1906 | + |
| 1907 | + if (list_empty(iova_copy)) |
| 1908 | + return; |
| 1909 | + |
| 1910 | + list_for_each_entry(domain, &iommu->domain_list, next) { |
| 1911 | + iommu_domain_get_attr(domain->domain, DOMAIN_ATTR_GEOMETRY, |
| 1912 | + &geo); |
| 1913 | + if (geo.aperture_start > start) |
| 1914 | + start = geo.aperture_start; |
| 1915 | + if (geo.aperture_end < end) |
| 1916 | + end = geo.aperture_end; |
| 1917 | + } |
| 1918 | + |
| 1919 | + /* Modify aperture limits. The new aper is either same or bigger */ |
| 1920 | + node = list_first_entry(iova_copy, struct vfio_iova, list); |
| 1921 | + node->start = start; |
| 1922 | + node = list_last_entry(iova_copy, struct vfio_iova, list); |
| 1923 | + node->end = end; |
| 1924 | +} |
| 1925 | + |
| 1926 | +/* |
| 1927 | + * Called when a group is detached. The reserved regions for that |
| 1928 | + * group can be part of valid iova now. But since reserved regions |
| 1929 | + * may be duplicated among groups, populate the iova valid regions |
| 1930 | + * list again. |
| 1931 | + */ |
| 1932 | +static int vfio_iommu_resv_refresh(struct vfio_iommu *iommu, |
| 1933 | + struct list_head *iova_copy) |
| 1934 | +{ |
| 1935 | + struct vfio_domain *d; |
| 1936 | + struct vfio_group *g; |
| 1937 | + struct vfio_iova *node; |
| 1938 | + dma_addr_t start, end; |
| 1939 | + LIST_HEAD(resv_regions); |
| 1940 | + int ret; |
| 1941 | + |
| 1942 | + if (list_empty(iova_copy)) |
| 1943 | + return -EINVAL; |
| 1944 | + |
| 1945 | + list_for_each_entry(d, &iommu->domain_list, next) { |
| 1946 | + list_for_each_entry(g, &d->group_list, next) { |
| 1947 | + ret = iommu_get_group_resv_regions(g->iommu_group, |
| 1948 | + &resv_regions); |
| 1949 | + if (ret) |
| 1950 | + goto done; |
| 1951 | + } |
| 1952 | + } |
| 1953 | + |
| 1954 | + node = list_first_entry(iova_copy, struct vfio_iova, list); |
| 1955 | + start = node->start; |
| 1956 | + node = list_last_entry(iova_copy, struct vfio_iova, list); |
| 1957 | + end = node->end; |
| 1958 | + |
| 1959 | + /* purge the iova list and create new one */ |
| 1960 | + vfio_iommu_iova_free(iova_copy); |
| 1961 | + |
| 1962 | + ret = vfio_iommu_aper_resize(iova_copy, start, end); |
| 1963 | + if (ret) |
| 1964 | + goto done; |
| 1965 | + |
| 1966 | + /* Exclude current reserved regions from iova ranges */ |
| 1967 | + ret = vfio_iommu_resv_exclude(iova_copy, &resv_regions); |
| 1968 | +done: |
| 1969 | + vfio_iommu_resv_free(&resv_regions); |
| 1970 | + return ret; |
| 1971 | +} |
| 1972 | + |
| 1598 | 1973 | static void vfio_iommu_type1_detach_group(void *iommu_data, |
| 1599 | 1974 | struct iommu_group *iommu_group) |
| 1600 | 1975 | { |
| 1601 | 1976 | struct vfio_iommu *iommu = iommu_data; |
| 1602 | 1977 | struct vfio_domain *domain; |
| 1603 | 1978 | struct vfio_group *group; |
| 1979 | + LIST_HEAD(iova_copy); |
| 1604 | 1980 | |
| 1605 | 1981 | mutex_lock(&iommu->lock); |
| 1606 | 1982 | |
| @@ -1623,6 +1999,13 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, · 16 unmodified lines |
| 1623 | 1999 | } |
| 1624 | 2000 | } |
| 1625 | 2001 | |
| 2002 | + /* |
| 2003 | + * Get a copy of iova list. This will be used to update |
| 2004 | + * and to replace the current one later. Please note that |
| 2005 | + * we will leave the original list as it is if update fails. |
| 2006 | + */ |
| 2007 | + vfio_iommu_iova_get_copy(iommu, &iova_copy); |
| 2008 | + |
| 1626 | 2009 | list_for_each_entry(domain, &iommu->domain_list, next) { |
| 1627 | 2010 | group = find_iommu_group(domain, iommu_group); |
| 1628 | 2011 | if (!group) |
| @@ -1648,10 +2031,16 @@ static void vfio_iommu_type1_detach_group(void *iommu_data, · 19 unmodified lines |
| 1648 | 2031 | iommu_domain_free(domain->domain); |
| 1649 | 2032 | list_del(&domain->next); |
| 1650 | 2033 | kfree(domain); |
| 2034 | + vfio_iommu_aper_expand(iommu, &iova_copy); |
| 1651 | 2035 | } |
| 1652 | 2036 | break; |
| 1653 | 2037 | } |
| 1654 | 2038 | |
| 2039 | + if (!vfio_iommu_resv_refresh(iommu, &iova_copy)) |
| 2040 | + vfio_iommu_iova_insert_copy(iommu, &iova_copy); |
| 2041 | + else |
| 2042 | + vfio_iommu_iova_free(&iova_copy); |
| 2043 | + |
| 1655 | 2044 | detach_group_done: |
| 1656 | 2045 | mutex_unlock(&iommu->lock); |
| 1657 | 2046 | } |
| @@ -1679,6 +2068,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) · 21 unmodified lines |
| 1679 | 2068 | } |
| 1680 | 2069 | |
| 1681 | 2070 | INIT_LIST_HEAD(&iommu->domain_list); |
| 2071 | + INIT_LIST_HEAD(&iommu->iova_list); |
| 1682 | 2072 | iommu->dma_list = RB_ROOT; |
| 1683 | 2073 | iommu->dma_avail = dma_entry_limit; |
| 1684 | 2074 | mutex_init(&iommu->lock); |
| @@ -1722,6 +2112,9 @@ static void vfio_iommu_type1_release(void *iommu_data) · 37 unmodified lines |
| 1722 | 2112 | list_del(&domain->next); |
| 1723 | 2113 | kfree(domain); |
| 1724 | 2114 | } |
| 2115 | + |
| 2116 | + vfio_iommu_iova_free(&iommu->iova_list); |
| 2117 | + |
| 1725 | 2118 | kfree(iommu); |
| 1726 | 2119 | } |
| 1727 | 2120 | |
| @@ -1742,6 +2135,73 @@ static int vfio_domains_have_iommu_cache(struct vfio_iommu *iommu) · 14 unmodified lines |
| 1742 | 2135 | return ret; |
| 1743 | 2136 | } |
| 1744 | 2137 | |
| 2138 | +static int vfio_iommu_iova_add_cap(struct vfio_info_cap *caps, |
| 2139 | + struct vfio_iommu_type1_info_cap_iova_range *cap_iovas, |
| 2140 | + size_t size) |
| 2141 | +{ |
| 2142 | + struct vfio_info_cap_header *header; |
| 2143 | + struct vfio_iommu_type1_info_cap_iova_range *iova_cap; |
| 2144 | + |
| 2145 | + header = vfio_info_cap_add(caps, size, |
| 2146 | + VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE, 1); |
| 2147 | + if (IS_ERR(header)) |
| 2148 | + return PTR_ERR(header); |
| 2149 | + |
| 2150 | + iova_cap = container_of(header, |
| 2151 | + struct vfio_iommu_type1_info_cap_iova_range, |
| 2152 | + header); |
| 2153 | + iova_cap->nr_iovas = cap_iovas->nr_iovas; |
| 2154 | + memcpy(iova_cap->iova_ranges, cap_iovas->iova_ranges, |
| 2155 | + cap_iovas->nr_iovas * sizeof(*cap_iovas->iova_ranges)); |
| 2156 | + return 0; |
| 2157 | +} |
| 2158 | + |
| 2159 | +static int vfio_iommu_iova_build_caps(struct vfio_iommu *iommu, |
| 2160 | + struct vfio_info_cap *caps) |
| 2161 | +{ |
| 2162 | + struct vfio_iommu_type1_info_cap_iova_range *cap_iovas; |
| 2163 | + struct vfio_iova *iova; |
| 2164 | + size_t size; |
| 2165 | + int iovas = 0, i = 0, ret; |
| 2166 | + |
| 2167 | + mutex_lock(&iommu->lock); |
| 2168 | + |
| 2169 | + list_for_each_entry(iova, &iommu->iova_list, list) |
| 2170 | + iovas++; |
| 2171 | + |
| 2172 | + if (!iovas) { |
| 2173 | + /* |
| 2174 | + * Return 0 as a container with a single mdev device |
| 2175 | + * will have an empty list |
| 2176 | + */ |
| 2177 | + ret = 0; |
| 2178 | + goto out_unlock; |
| 2179 | + } |
| 2180 | + |
| 2181 | + size = sizeof(*cap_iovas) + (iovas * sizeof(*cap_iovas->iova_ranges)); |
| 2182 | + |
| 2183 | + cap_iovas = kzalloc(size, GFP_KERNEL); |
| 2184 | + if (!cap_iovas) { |
| 2185 | + ret = -ENOMEM; |
| 2186 | + goto out_unlock; |
| 2187 | + } |
| 2188 | + |
| 2189 | + cap_iovas->nr_iovas = iovas; |
| 2190 | + |
| 2191 | + list_for_each_entry(iova, &iommu->iova_list, list) { |
| 2192 | + cap_iovas->iova_ranges[i].start = iova->start; |
| 2193 | + cap_iovas->iova_ranges[i].end = iova->end; |
| 2194 | + i++; |
| 2195 | + } |
| 2196 | + |
| 2197 | + ret = vfio_iommu_iova_add_cap(caps, cap_iovas, size); |
| 2198 | + |
| 2199 | + kfree(cap_iovas); |
| 2200 | +out_unlock: |
| 2201 | + mutex_unlock(&iommu->lock); |
| 2202 | + return ret; |
| 2203 | +} |
| 2204 | + |
| 1745 | 2205 | static long vfio_iommu_type1_ioctl(void *iommu_data, |
| 1746 | 2206 | unsigned int cmd, unsigned long arg) |
| 1747 | 2207 | { |
| @@ -1763,19 +2223,53 @@ static long vfio_iommu_type1_ioctl(void *iommu_data, · 15 unmodified lines |
| 1763 | 2223 | } |
| 1764 | 2224 | } else if (cmd == VFIO_IOMMU_GET_INFO) { |
| 1765 | 2225 | struct vfio_iommu_type1_info info; |
| 2226 | + struct vfio_info_cap caps = { .buf = NULL, .size = 0 }; |
| 2227 | + unsigned long capsz; |
| 2228 | + int ret; |
| 1766 | 2229 | |
| 1767 | 2230 | minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes); |
| 1768 | 2231 | |
| 2232 | + /* For backward compatibility, cannot require this */ |
| 2233 | + capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset); |
| 2234 | + |
| 1769 | 2235 | if (copy_from_user(&info, (void __user *)arg, minsz)) |
| 1770 | 2236 | return -EFAULT; |
| 1771 | 2237 | |
| 1772 | 2238 | if (info.argsz < minsz) |
| 1773 | 2239 | return -EINVAL; |
| 1774 | 2240 | |
| 2241 | + if (info.argsz >= capsz) { |
| 2242 | + minsz = capsz; |
| 2243 | + info.cap_offset = 0; /* output, no-recopy necessary */ |
| 2244 | + } |
| 2245 | + |
| 1775 | 2246 | info.flags = VFIO_IOMMU_INFO_PGSIZES; |
| 1776 | 2247 | |
| 1777 | 2248 | info.iova_pgsizes = vfio_pgsize_bitmap(iommu); |
| 1778 | 2249 | |
| 2250 | + ret = vfio_iommu_iova_build_caps(iommu, &caps); |
| 2251 | + if (ret) |
| 2252 | + return ret; |
| 2253 | + |
| 2254 | + if (caps.size) { |
| 2255 | + info.flags |= VFIO_IOMMU_INFO_CAPS; |
| 2256 | + |
| 2257 | + if (info.argsz < sizeof(info) + caps.size) { |
| 2258 | + info.argsz = sizeof(info) + caps.size; |
| 2259 | + } else { |
| 2260 | + vfio_info_cap_shift(&caps, sizeof(info)); |
| 2261 | + if (copy_to_user((void __user *)arg + |
| 2262 | + sizeof(info), caps.buf, |
| 2263 | + caps.size)) { |
| 2264 | + kfree(caps.buf); |
| 2265 | + return -EFAULT; |
| 2266 | + } |
| 2267 | + info.cap_offset = sizeof(info); |
| 2268 | + } |
| 2269 | + |
| 2270 | + kfree(caps.buf); |
| 2271 | + } |
| 2272 | + |
| 1779 | 2273 | return copy_to_user((void __user *)arg, &info, minsz) ? |
| 1780 | 2274 | -EFAULT : 0; |
| 1781 | 2275 | |