aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm
diff options
context:
space:
mode:
authorDavid Hildenbrand <dahi@linux.vnet.ibm.com>2016-03-08 06:23:38 -0500
committerChristian Borntraeger <borntraeger@de.ibm.com>2016-06-20 03:54:27 -0400
commit998f637cc4b9ef3fa32b196294a3136ee05271a2 (patch)
tree4877cf772f2b98f258578bb048ba6ed52ee77c2b /arch/s390/mm
parenta9d23e71d7716e394a772686bfd994f4e181b235 (diff)
s390/mm: avoid races on region/segment/page table shadowing
We have to unlock sg->guest_table_lock in order to call gmap_protect_rmap(). If we sleep just before that call, another VCPU might pick up that shadowed page table (while it is not protected yet) and use it. In order to avoid these races, we have to introduce a third state - "origin set but still invalid" for an entry. This way, we can avoid another thread already using the entry before the table is fully protected. As soon as everything is set up, we can clear the invalid bit - if we had no race with the unshadowing code. Suggested-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Diffstat (limited to 'arch/s390/mm')
-rw-r--r--arch/s390/mm/gmap.c97
1 files changed, 70 insertions, 27 deletions
diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c
index a57a87bfeb27..a396e58b5a43 100644
--- a/arch/s390/mm/gmap.c
+++ b/arch/s390/mm/gmap.c
@@ -1125,7 +1125,7 @@ static void gmap_unshadow_pgt(struct gmap *sg, unsigned long raddr)
1125 1125
1126 BUG_ON(!gmap_is_shadow(sg)); 1126 BUG_ON(!gmap_is_shadow(sg));
1127 ste = gmap_table_walk(sg, raddr, 1); /* get segment pointer */ 1127 ste = gmap_table_walk(sg, raddr, 1); /* get segment pointer */
1128 if (!ste || *ste & _SEGMENT_ENTRY_INVALID) 1128 if (!ste || !(*ste & _SEGMENT_ENTRY_ORIGIN))
1129 return; 1129 return;
1130 gmap_call_notifier(sg, raddr, raddr + (1UL << 20) - 1); 1130 gmap_call_notifier(sg, raddr, raddr + (1UL << 20) - 1);
1131 sto = (unsigned long) (ste - ((raddr >> 20) & 0x7ff)); 1131 sto = (unsigned long) (ste - ((raddr >> 20) & 0x7ff));
@@ -1157,7 +1157,7 @@ static void __gmap_unshadow_sgt(struct gmap *sg, unsigned long raddr,
1157 BUG_ON(!gmap_is_shadow(sg)); 1157 BUG_ON(!gmap_is_shadow(sg));
1158 asce = (unsigned long) sgt | _ASCE_TYPE_SEGMENT; 1158 asce = (unsigned long) sgt | _ASCE_TYPE_SEGMENT;
1159 for (i = 0; i < 2048; i++, raddr += 1UL << 20) { 1159 for (i = 0; i < 2048; i++, raddr += 1UL << 20) {
1160 if (sgt[i] & _SEGMENT_ENTRY_INVALID) 1160 if (!(sgt[i] & _SEGMENT_ENTRY_ORIGIN))
1161 continue; 1161 continue;
1162 pgt = (unsigned long *)(sgt[i] & _REGION_ENTRY_ORIGIN); 1162 pgt = (unsigned long *)(sgt[i] & _REGION_ENTRY_ORIGIN);
1163 sgt[i] = _SEGMENT_ENTRY_EMPTY; 1163 sgt[i] = _SEGMENT_ENTRY_EMPTY;
@@ -1183,7 +1183,7 @@ static void gmap_unshadow_sgt(struct gmap *sg, unsigned long raddr)
1183 1183
1184 BUG_ON(!gmap_is_shadow(sg)); 1184 BUG_ON(!gmap_is_shadow(sg));
1185 r3e = gmap_table_walk(sg, raddr, 2); /* get region-3 pointer */ 1185 r3e = gmap_table_walk(sg, raddr, 2); /* get region-3 pointer */
1186 if (!r3e || *r3e & _REGION_ENTRY_INVALID) 1186 if (!r3e || !(*r3e & _REGION_ENTRY_ORIGIN))
1187 return; 1187 return;
1188 gmap_call_notifier(sg, raddr, raddr + (1UL << 31) - 1); 1188 gmap_call_notifier(sg, raddr, raddr + (1UL << 31) - 1);
1189 r3o = (unsigned long) (r3e - ((raddr >> 31) & 0x7ff)); 1189 r3o = (unsigned long) (r3e - ((raddr >> 31) & 0x7ff));
@@ -1215,7 +1215,7 @@ static void __gmap_unshadow_r3t(struct gmap *sg, unsigned long raddr,
1215 BUG_ON(!gmap_is_shadow(sg)); 1215 BUG_ON(!gmap_is_shadow(sg));
1216 asce = (unsigned long) r3t | _ASCE_TYPE_REGION3; 1216 asce = (unsigned long) r3t | _ASCE_TYPE_REGION3;
1217 for (i = 0; i < 2048; i++, raddr += 1UL << 31) { 1217 for (i = 0; i < 2048; i++, raddr += 1UL << 31) {
1218 if (r3t[i] & _REGION_ENTRY_INVALID) 1218 if (!(r3t[i] & _REGION_ENTRY_ORIGIN))
1219 continue; 1219 continue;
1220 sgt = (unsigned long *)(r3t[i] & _REGION_ENTRY_ORIGIN); 1220 sgt = (unsigned long *)(r3t[i] & _REGION_ENTRY_ORIGIN);
1221 r3t[i] = _REGION3_ENTRY_EMPTY; 1221 r3t[i] = _REGION3_ENTRY_EMPTY;
@@ -1241,7 +1241,7 @@ static void gmap_unshadow_r3t(struct gmap *sg, unsigned long raddr)
1241 1241
1242 BUG_ON(!gmap_is_shadow(sg)); 1242 BUG_ON(!gmap_is_shadow(sg));
1243 r2e = gmap_table_walk(sg, raddr, 3); /* get region-2 pointer */ 1243 r2e = gmap_table_walk(sg, raddr, 3); /* get region-2 pointer */
1244 if (!r2e || *r2e & _REGION_ENTRY_INVALID) 1244 if (!r2e || !(*r2e & _REGION_ENTRY_ORIGIN))
1245 return; 1245 return;
1246 gmap_call_notifier(sg, raddr, raddr + (1UL << 42) - 1); 1246 gmap_call_notifier(sg, raddr, raddr + (1UL << 42) - 1);
1247 r2o = (unsigned long) (r2e - ((raddr >> 42) & 0x7ff)); 1247 r2o = (unsigned long) (r2e - ((raddr >> 42) & 0x7ff));
@@ -1273,7 +1273,7 @@ static void __gmap_unshadow_r2t(struct gmap *sg, unsigned long raddr,
1273 BUG_ON(!gmap_is_shadow(sg)); 1273 BUG_ON(!gmap_is_shadow(sg));
1274 asce = (unsigned long) r2t | _ASCE_TYPE_REGION2; 1274 asce = (unsigned long) r2t | _ASCE_TYPE_REGION2;
1275 for (i = 0; i < 2048; i++, raddr += 1UL << 42) { 1275 for (i = 0; i < 2048; i++, raddr += 1UL << 42) {
1276 if (r2t[i] & _REGION_ENTRY_INVALID) 1276 if (!(r2t[i] & _REGION_ENTRY_ORIGIN))
1277 continue; 1277 continue;
1278 r3t = (unsigned long *)(r2t[i] & _REGION_ENTRY_ORIGIN); 1278 r3t = (unsigned long *)(r2t[i] & _REGION_ENTRY_ORIGIN);
1279 r2t[i] = _REGION2_ENTRY_EMPTY; 1279 r2t[i] = _REGION2_ENTRY_EMPTY;
@@ -1299,7 +1299,7 @@ static void gmap_unshadow_r2t(struct gmap *sg, unsigned long raddr)
1299 1299
1300 BUG_ON(!gmap_is_shadow(sg)); 1300 BUG_ON(!gmap_is_shadow(sg));
1301 r1e = gmap_table_walk(sg, raddr, 4); /* get region-1 pointer */ 1301 r1e = gmap_table_walk(sg, raddr, 4); /* get region-1 pointer */
1302 if (!r1e || *r1e & _REGION_ENTRY_INVALID) 1302 if (!r1e || !(*r1e & _REGION_ENTRY_ORIGIN))
1303 return; 1303 return;
1304 gmap_call_notifier(sg, raddr, raddr + (1UL << 53) - 1); 1304 gmap_call_notifier(sg, raddr, raddr + (1UL << 53) - 1);
1305 r1o = (unsigned long) (r1e - ((raddr >> 53) & 0x7ff)); 1305 r1o = (unsigned long) (r1e - ((raddr >> 53) & 0x7ff));
@@ -1331,7 +1331,7 @@ static void __gmap_unshadow_r1t(struct gmap *sg, unsigned long raddr,
1331 BUG_ON(!gmap_is_shadow(sg)); 1331 BUG_ON(!gmap_is_shadow(sg));
1332 asce = (unsigned long) r1t | _ASCE_TYPE_REGION1; 1332 asce = (unsigned long) r1t | _ASCE_TYPE_REGION1;
1333 for (i = 0; i < 2048; i++, raddr += 1UL << 53) { 1333 for (i = 0; i < 2048; i++, raddr += 1UL << 53) {
1334 if (r1t[i] & _REGION_ENTRY_INVALID) 1334 if (!(r1t[i] & _REGION_ENTRY_ORIGIN))
1335 continue; 1335 continue;
1336 r2t = (unsigned long *)(r1t[i] & _REGION_ENTRY_ORIGIN); 1336 r2t = (unsigned long *)(r1t[i] & _REGION_ENTRY_ORIGIN);
1337 __gmap_unshadow_r2t(sg, raddr, r2t); 1337 __gmap_unshadow_r2t(sg, raddr, r2t);
@@ -1496,10 +1496,14 @@ int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t)
1496 if (!(*table & _REGION_ENTRY_INVALID)) { 1496 if (!(*table & _REGION_ENTRY_INVALID)) {
1497 rc = 0; /* Already established */ 1497 rc = 0; /* Already established */
1498 goto out_free; 1498 goto out_free;
1499 } else if (*table & _REGION_ENTRY_ORIGIN) {
1500 rc = -EAGAIN; /* Race with shadow */
1501 goto out_free;
1499 } 1502 }
1500 crst_table_init(s_r2t, _REGION2_ENTRY_EMPTY); 1503 crst_table_init(s_r2t, _REGION2_ENTRY_EMPTY);
1501 *table = (unsigned long) s_r2t | 1504 /* mark as invalid as long as the parent table is not protected */
1502 _REGION_ENTRY_LENGTH | _REGION_ENTRY_TYPE_R1; 1505 *table = (unsigned long) s_r2t | _REGION_ENTRY_LENGTH |
1506 _REGION_ENTRY_TYPE_R1 | _REGION_ENTRY_INVALID;
1503 list_add(&page->lru, &sg->crst_list); 1507 list_add(&page->lru, &sg->crst_list);
1504 spin_unlock(&sg->guest_table_lock); 1508 spin_unlock(&sg->guest_table_lock);
1505 /* Make r2t read-only in parent gmap page table */ 1509 /* Make r2t read-only in parent gmap page table */
@@ -1508,11 +1512,18 @@ int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t)
1508 offset = ((r2t & _REGION_ENTRY_OFFSET) >> 6) * 4096; 1512 offset = ((r2t & _REGION_ENTRY_OFFSET) >> 6) * 4096;
1509 len = ((r2t & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset; 1513 len = ((r2t & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset;
1510 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ); 1514 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
1511 if (rc) { 1515 spin_lock(&sg->guest_table_lock);
1512 spin_lock(&sg->guest_table_lock); 1516 if (!rc) {
1517 table = gmap_table_walk(sg, saddr, 4);
1518 if (!table || (*table & _REGION_ENTRY_ORIGIN) !=
1519 (unsigned long) s_r2t)
1520 rc = -EAGAIN; /* Race with unshadow */
1521 else
1522 *table &= ~_REGION_ENTRY_INVALID;
1523 } else {
1513 gmap_unshadow_r2t(sg, raddr); 1524 gmap_unshadow_r2t(sg, raddr);
1514 spin_unlock(&sg->guest_table_lock);
1515 } 1525 }
1526 spin_unlock(&sg->guest_table_lock);
1516 return rc; 1527 return rc;
1517out_free: 1528out_free:
1518 spin_unlock(&sg->guest_table_lock); 1529 spin_unlock(&sg->guest_table_lock);
@@ -1557,10 +1568,13 @@ int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t)
1557 if (!(*table & _REGION_ENTRY_INVALID)) { 1568 if (!(*table & _REGION_ENTRY_INVALID)) {
1558 rc = 0; /* Already established */ 1569 rc = 0; /* Already established */
1559 goto out_free; 1570 goto out_free;
1571 } else if (*table & _REGION_ENTRY_ORIGIN) {
1572 rc = -EAGAIN; /* Race with shadow */
1560 } 1573 }
1561 crst_table_init(s_r3t, _REGION3_ENTRY_EMPTY); 1574 crst_table_init(s_r3t, _REGION3_ENTRY_EMPTY);
1562 *table = (unsigned long) s_r3t | 1575 /* mark as invalid as long as the parent table is not protected */
1563 _REGION_ENTRY_LENGTH | _REGION_ENTRY_TYPE_R2; 1576 *table = (unsigned long) s_r3t | _REGION_ENTRY_LENGTH |
1577 _REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_INVALID;
1564 list_add(&page->lru, &sg->crst_list); 1578 list_add(&page->lru, &sg->crst_list);
1565 spin_unlock(&sg->guest_table_lock); 1579 spin_unlock(&sg->guest_table_lock);
1566 /* Make r3t read-only in parent gmap page table */ 1580 /* Make r3t read-only in parent gmap page table */
@@ -1569,11 +1583,18 @@ int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t)
1569 offset = ((r3t & _REGION_ENTRY_OFFSET) >> 6) * 4096; 1583 offset = ((r3t & _REGION_ENTRY_OFFSET) >> 6) * 4096;
1570 len = ((r3t & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset; 1584 len = ((r3t & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset;
1571 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ); 1585 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
1572 if (rc) { 1586 spin_lock(&sg->guest_table_lock);
1573 spin_lock(&sg->guest_table_lock); 1587 if (!rc) {
1588 table = gmap_table_walk(sg, saddr, 3);
1589 if (!table || (*table & _REGION_ENTRY_ORIGIN) !=
1590 (unsigned long) s_r3t)
1591 rc = -EAGAIN; /* Race with unshadow */
1592 else
1593 *table &= ~_REGION_ENTRY_INVALID;
1594 } else {
1574 gmap_unshadow_r3t(sg, raddr); 1595 gmap_unshadow_r3t(sg, raddr);
1575 spin_unlock(&sg->guest_table_lock);
1576 } 1596 }
1597 spin_unlock(&sg->guest_table_lock);
1577 return rc; 1598 return rc;
1578out_free: 1599out_free:
1579 spin_unlock(&sg->guest_table_lock); 1600 spin_unlock(&sg->guest_table_lock);
@@ -1618,10 +1639,14 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt)
1618 if (!(*table & _REGION_ENTRY_INVALID)) { 1639 if (!(*table & _REGION_ENTRY_INVALID)) {
1619 rc = 0; /* Already established */ 1640 rc = 0; /* Already established */
1620 goto out_free; 1641 goto out_free;
1642 } else if (*table & _REGION_ENTRY_ORIGIN) {
1643 rc = -EAGAIN; /* Race with shadow */
1644 goto out_free;
1621 } 1645 }
1622 crst_table_init(s_sgt, _SEGMENT_ENTRY_EMPTY); 1646 crst_table_init(s_sgt, _SEGMENT_ENTRY_EMPTY);
1623 *table = (unsigned long) s_sgt | 1647 /* mark as invalid as long as the parent table is not protected */
1624 _REGION_ENTRY_LENGTH | _REGION_ENTRY_TYPE_R3; 1648 *table = (unsigned long) s_sgt | _REGION_ENTRY_LENGTH |
1649 _REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INVALID;
1625 list_add(&page->lru, &sg->crst_list); 1650 list_add(&page->lru, &sg->crst_list);
1626 spin_unlock(&sg->guest_table_lock); 1651 spin_unlock(&sg->guest_table_lock);
1627 /* Make sgt read-only in parent gmap page table */ 1652 /* Make sgt read-only in parent gmap page table */
@@ -1630,11 +1655,18 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt)
1630 offset = ((sgt & _REGION_ENTRY_OFFSET) >> 6) * 4096; 1655 offset = ((sgt & _REGION_ENTRY_OFFSET) >> 6) * 4096;
1631 len = ((sgt & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset; 1656 len = ((sgt & _REGION_ENTRY_LENGTH) + 1) * 4096 - offset;
1632 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ); 1657 rc = gmap_protect_rmap(sg, raddr, origin + offset, len, PROT_READ);
1633 if (rc) { 1658 spin_lock(&sg->guest_table_lock);
1634 spin_lock(&sg->guest_table_lock); 1659 if (!rc) {
1660 table = gmap_table_walk(sg, saddr, 2);
1661 if (!table || (*table & _REGION_ENTRY_ORIGIN) !=
1662 (unsigned long) s_sgt)
1663 rc = -EAGAIN; /* Race with unshadow */
1664 else
1665 *table &= ~_REGION_ENTRY_INVALID;
1666 } else {
1635 gmap_unshadow_sgt(sg, raddr); 1667 gmap_unshadow_sgt(sg, raddr);
1636 spin_unlock(&sg->guest_table_lock);
1637 } 1668 }
1669 spin_unlock(&sg->guest_table_lock);
1638 return rc; 1670 return rc;
1639out_free: 1671out_free:
1640 spin_unlock(&sg->guest_table_lock); 1672 spin_unlock(&sg->guest_table_lock);
@@ -1716,20 +1748,31 @@ int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt)
1716 if (!(*table & _SEGMENT_ENTRY_INVALID)) { 1748 if (!(*table & _SEGMENT_ENTRY_INVALID)) {
1717 rc = 0; /* Already established */ 1749 rc = 0; /* Already established */
1718 goto out_free; 1750 goto out_free;
1751 } else if (*table & _SEGMENT_ENTRY_ORIGIN) {
1752 rc = -EAGAIN; /* Race with shadow */
1753 goto out_free;
1719 } 1754 }
1755 /* mark as invalid as long as the parent table is not protected */
1720 *table = (unsigned long) s_pgt | _SEGMENT_ENTRY | 1756 *table = (unsigned long) s_pgt | _SEGMENT_ENTRY |
1721 (pgt & _SEGMENT_ENTRY_PROTECT); 1757 (pgt & _SEGMENT_ENTRY_PROTECT) | _SEGMENT_ENTRY_INVALID;
1722 list_add(&page->lru, &sg->pt_list); 1758 list_add(&page->lru, &sg->pt_list);
1723 spin_unlock(&sg->guest_table_lock); 1759 spin_unlock(&sg->guest_table_lock);
1724 /* Make pgt read-only in parent gmap page table (not the pgste) */ 1760 /* Make pgt read-only in parent gmap page table (not the pgste) */
1725 raddr = (saddr & 0xfffffffffff00000UL) | _SHADOW_RMAP_SEGMENT; 1761 raddr = (saddr & 0xfffffffffff00000UL) | _SHADOW_RMAP_SEGMENT;
1726 origin = pgt & _SEGMENT_ENTRY_ORIGIN & PAGE_MASK; 1762 origin = pgt & _SEGMENT_ENTRY_ORIGIN & PAGE_MASK;
1727 rc = gmap_protect_rmap(sg, raddr, origin, PAGE_SIZE, PROT_READ); 1763 rc = gmap_protect_rmap(sg, raddr, origin, PAGE_SIZE, PROT_READ);
1728 if (rc) { 1764 spin_lock(&sg->guest_table_lock);
1729 spin_lock(&sg->guest_table_lock); 1765 if (!rc) {
1766 table = gmap_table_walk(sg, saddr, 1);
1767 if (!table || (*table & _SEGMENT_ENTRY_ORIGIN) !=
1768 (unsigned long) s_pgt)
1769 rc = -EAGAIN; /* Race with unshadow */
1770 else
1771 *table &= ~_SEGMENT_ENTRY_INVALID;
1772 } else {
1730 gmap_unshadow_pgt(sg, raddr); 1773 gmap_unshadow_pgt(sg, raddr);
1731 spin_unlock(&sg->guest_table_lock);
1732 } 1774 }
1775 spin_unlock(&sg->guest_table_lock);
1733 return rc; 1776 return rc;
1734out_free: 1777out_free:
1735 spin_unlock(&sg->guest_table_lock); 1778 spin_unlock(&sg->guest_table_lock);