Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 | /* * Copyright (c) 2024 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #ifndef _VM_VM_MAP_LOCK_H_ #define _VM_VM_MAP_LOCK_H_ #include <stdbool.h> #include <kern/locks.h> #include <kern/lock_mtx.h> #include <vm/vm_map_internal.h> __BEGIN_DECLS #if DEVELOPMENT || DEBUG #define RANGE_LOCK_DEBUG #endif /* DEVELOPMENT || DEBUG */ #ifdef RANGE_LOCK_DEBUG #define RANGE_LOCK_ASSERT(cond) assert(cond) #else #define RANGE_LOCK_ASSERT(cond) #endif #define err_vm_map_lock(e) (err_vm | err_sub(2) | (e)) /*! denote in a preflight hook that this entry shouldn't be prepared */ #define VMRL_ERR_SKIP_PREPARE err_vm_map_lock(1) /*! denote in a preflight hook that the caller wants to wait for unwire */ #define VMRL_ERR_WAIT_FOR_KUNWIRE err_vm_map_lock(2) /*! denote in a preflight hook that the caller wants symmetric COW setup */ #define VMRL_ERR_SETUP_SYMMETRIC_COW err_vm_map_lock(3) /*! same as VMRL_ERR_SETUP_SYMMETRIC_COW, but the entry on which COW is setup is not clipped */ #define VMRL_ERR_SETUP_SYMMETRIC_COW_NOCLIP err_vm_map_lock(4) /*! denote in a preflight hook that the entry is going to be shared with another map */ #define VMRL_ERR_PREPARE_FOR_SHARE_NOCLIP err_vm_map_lock(5) /*! denote in a preflight hook that the entry is going to be shared, * that we can clip, and that we should apply UPL optimizations */ #define VMRL_ERR_PREPARE_FOR_SHARE_WITH_UPL err_vm_map_lock(6) /*! denote from the lock that a try_lock failed because a lock was already held */ #define VMRL_ERR_LOCK_ALREADY_HELD err_vm_map_lock(7) /* * Explanation of the two types of submaps: * There are two types of submaps supported by the VM today. * For both types, nested submaps are disallowed, which means that a submap cannot * have an entry that is a submap itself. * * 1) constant submaps * Constant submaps are submaps that have static vm_map_entry_t's. * That means once "sealed", there is a guarantee that no vm_map_entry_t in that * submap will change in any way. This means no locks need to be held on constant submaps. * When descending into a constant submap, address transformation is required * (see vm_map_parent_address_to_submap_address). * Today's examples of these are the shared cache and the x86 commpage. * * 2) transparent submaps * Transparent submaps are submaps created by kmem_suballoc(). They are mapped * only in the kernel_map and use the kernel_pmap. Transparent submaps cannot be * mapped in multiple entries. They are guaranteed to have VME_OFFSET(entry) == entry->vme_start * for the submap entry, which means there is no address transformation required when descending * into them (e.g. address A in a parent map is also address A in the submap). That, combined * with the automatic descension by the VM into transparent submaps * justify the name "transparent" (they are transparent to clients and the VM). * An entry mapping a transparent submap must be atomic and permanent. * examples include the ipc_kernel_map submap and the compressor submap * * The range lock does not permit being called directly on a constant submap * once it has been sealed, as it does not lock constant submap entries. * Transparent submaps are allowed. */ #if MACH_ASSERT /* * Debugging validation of a map when its interlock is locked or unlocked. * These checks exist in development builds only. * Fast checks (always on) go in the inline functions. * Slow checks (enabled by boot-arg) go in the extern functions. * We also check mach_assert_enabled() to allow static_if to optimize * these fast paths. */ extern bool vm_debug_any_options_enabled; static inline OS_ALWAYS_INLINE void vm_map_debug_after_lock_fast(vm_map_t map) { if (mach_assert_enabled() && vm_debug_any_options_enabled) { (void)map; } } static inline OS_ALWAYS_INLINE void vm_map_debug_before_unlock_fast(vm_map_t map) { if (mach_assert_enabled() && vm_debug_any_options_enabled) { (void)map; } } #else /* not MACH_ASSERT */ #define vm_map_debug_after_lock_fast(map) ({}) #define vm_map_debug_before_unlock_fast(map) ({}) #endif /* not MACH_ASSERT */ #ifndef VM_MAP_LOCK_PRIVATE /*! * @function vm_map_ilk_lock() * * @brief * Takes the interlock of the specified map. * * @discussion * The interlock protects non-atomic variables on the vm_map_t such as * the rb-tree or map->size */ static inline void vm_map_ilk_lock(vm_map_t map) { assert(!vm_map_is_sealed(map)); lck_rw_lock_exclusive(&map->ilock); vm_map_debug_after_lock_fast(map); } /*! * @function vm_map_ilk_unlock() * * @brief * Drops the interlock of the specified map. */ static inline void vm_map_ilk_unlock(vm_map_t map) { assert(!vm_map_is_sealed(map)); vm_map_debug_before_unlock_fast(map); lck_rw_unlock_exclusive(&map->ilock); } /*! * @function vm_map_ilk_lock_shared() * * @brief * Takes the interlock of the specified map in shared mode. * * @discussion * The interlock protects non-atomic variables on the vm_map_t such as * the rb-tree or map->size */ static inline void vm_map_ilk_lock_shared(vm_map_t map) { assert(!vm_map_is_sealed(map)); lck_rw_lock_shared(&map->ilock); } /*! * @function vm_map_ilk_unlock_shared() * * @brief * Drops the interlock of the specified map. */ static inline void vm_map_ilk_unlock_shared(vm_map_t map) { assert(!vm_map_is_sealed(map)); lck_rw_unlock_shared(&map->ilock); } /*! * @function vm_map_ilk_lock_allow_sealed() * * @brief * Takes the interlock of the specified map, allowed on sealed maps. * * @discussion * This is the same as @c vm_map_ilk_lock(), but allows for sealed maps, * which is useful to handle sealing/unsealing transitions. */ static inline void vm_map_ilk_lock_allow_sealed(vm_map_t map) { lck_rw_lock_exclusive(&map->ilock); vm_map_debug_after_lock_fast(map); } /*! * @function vm_map_ilk_unlock_allow_sealed() * * @brief * Drops the interlock of the specified map, allowed on sealed maps. * * @discussion * This is the same as @c vm_map_ilk_unlock(), but allows for sealed maps, * which is useful to handle sealing/unsealing transitions. */ static inline void vm_map_ilk_unlock_allow_sealed(vm_map_t map) { vm_map_debug_before_unlock_fast(map); lck_rw_unlock_exclusive(&map->ilock); } /*! * @function vm_map_ilk_sleep() * * @brief * Sleep while releasing the map interlock, waiting for a wakeup on an event. * Upon return, the map interlock is held again. */ static inline wait_result_t vm_map_ilk_sleep(vm_map_t map, event_t event, wait_interrupt_t interruptible) { wait_result_t wr; vm_map_debug_before_unlock_fast(map); wr = lck_rw_sleep(&map->ilock, LCK_SLEEP_DEFAULT, event, interruptible); vm_map_debug_after_lock_fast(map); return wr; } static inline void assert_vm_map_ilk_owned(vm_map_t map, lck_rw_type_t how) { (void) map; if (lck_rw_assert_enabled()) { lck_rw_assert_held_type(&map->ilock, how); } } static inline void assert_vm_map_ilk_not_owned(vm_map_t map __assert_only) { LCK_RW_ASSERT(&map->ilock, LCK_RW_ASSERT_NOT_OWNED); } #else /* !VM_MAP_LOCK_PRIVATE */ /* Internal errors to the vm map/entry locks */ #define VMRL_ERR_RELOOKUP err_vm_map_lock(0x101) #define VMRL_ERR_ABORTED err_vm_map_lock(0x102) #define VMRL_ERR_NOT_FOUND err_vm_map_lock(0x103) #endif /* !VM_MAP_LOCK_PRIVATE */ static inline void assert_vm_map_ilk_owned_ignore_sealed(vm_map_t map, lck_rw_type_t how) { if (!vm_map_is_sealed_or_will_be_sealed(map) && lck_rw_assert_enabled()) { lck_rw_assert_held_type(&map->ilock, how); } } /*! * @function vm_map_has_entry_at_address_ilocked() * * @brief * Checks whether an entry is present in the provided map at the provided * address. Should be called with the interlock held, returns with the * interlock held. Doesn't drop the interlock. */ static inline bool vm_map_has_entry_at_address_ilocked( vm_map_t map, vm_map_offset_t addr) { return vm_map_lookup(map, addr) != VM_MAP_ENTRY_NULL; } /*! * @typedef vmrl_flags_t * * @brief * Flags affecting the behavior of acquiring a range lock. * * * <h1>locking mode, exactly one must be selected</h1> * * @const VMRL_STREAM (ex, sh) * For cases where we want to allow holes (for example: vm_map_inherit(), * vm_map_delete() when used for termination, and most uses of shared locking), * make the lock stream rather than lock the whole range. This means * the lock's behavior is not atomic and instead the next entry is locked * on advancing entries. * * Note that when streaming there is no guarantee that entries being returned * move "forward" without overlaps as concurrent mutations of the map might * cause coalescing concurrently. As a result it is important to query the * bounds to apply to the returned entry in code handling entries using * @c vm_map_lock_ctx_bounds() or similar. * * @const VMRL_STREAM_NO_HOLES (ex, sh) * Similar to VMRL_STREAM, however, when a hole has been detected, * the iteration will fail with KERN_INVALID_ADDRESS. * * @const VMRL_ATOMIC (ex, sh) * Denotes an atomic (i.e. non-streaming) lock. The whole range will be held * locked for the entire duration of the operation (from lock to unlock) and * holes will not be allowed (attempting to lock a range with holes will return * an error). * * @const VMRL_ATOMIC_ALLOW_HOLES (ex) * Similar to @c VMRL_ATOMIC but allows for holes by inserting sentinel entries * to denote locked holes. * * They will be returned during iteration as placeholders that clients must * ignore, however. * * This should only be used to implement fixed overwrite mappings in the context * of enter/delete. Other APIs should stream or use VMRL_ATOMIC. This is because * sentinel entries take up space that is not available for other allocations, * which could potentially cause them to fail spuriously. * * * <h1>shared/exclusive locking, exactly one must be selected</h1> * * Note that this flag is selected implicitly via the usage of @c VMRL_EX_* * versus @c VMRL_SH_* namespaced flags. * * @const VMRL_SHARED (implicit: sh) * Denotes a shared lock. * * @const VMRL_EXCLUSIVE (implicit: ex) * Denotes an exclusive lock. * * * <h1>Entry preparation<h1> * * @const VMRL_VMO_ALLOCATE (sh) * Allocate a vm object if VME_OBJECT(entry) == NULL. Do not resolve CoW. * * @const VMRL_RESOLVE_COW_AND_OBJ * Resolve the vm_object or submap of each entry, which means for * 1) entries with a NULL object: allocate one. * 2) entries with needs_copy=true: resolve object level or submap level CoW. * * Clients of the range lock will be given an entry with needs_copy=false and * an object or submap associated. * This will do pmap unnesting if a submap needs to be resolved. For resolving * submap CoW, it essentially copies the submap entries into the top level map * and creates a shadow object. * For object CoW, it just creates a shadow object. * * @const VMRL_NO_PMAP_UNNEST (ex) * Do not unecessarily pmap unnest any submap entries within the range lock. * Those entries will still be pmap_unnested if a clip would need to be performed. * * <h1>Behavior modifiers<h1> * * @const VMRL_NO_MIN_MAX_CHECK (ex, sh) * Allow the range to go beyond the map [min_offset, max_offset). * Without this flag, locks will fail with KERN_INVALID_ADDRESS * if the requested range goes beyond this range. * This should not be used with VMRL_WHOLE_MAP since they mean similar things. * * @const VMRL_WHOLE_MAP (ex, sh) * Lock the entire virtual address range, including entries beyond * [min_offset, max_offset). * start and end of the lock must be set to VMRL_WHOLE_MAP_START, VMRL_WHOLE_MAP_END * This should not be used with VMRL_NO_MIN_MAX_CHECK since they mean similar things. * * @const VMRL_SIMPLIFY (ex) * Whether entry coalescing/simplification should be attempted. * This happens at unlock time for ATOMIC locks and advance or drop time for * STREAM locks. * * @const VMRL_INTERRUPTIBLE (ex, sh) * Whether waiting for a lock is interruptible. When interrupted, * the lock acquiring or enumeration will fail with KERN_ABORTED. * * @const VMRL_ILK_LOCKED (ex, sh) * Whether the interlock of the map is held at the time * of lock acquisition, functions will always return with * it unlocked. * * @const VMRL_TRY_LOCK_ENTRY (ex,sh) * Do a vm_entry_try_lock_* on the entry rather than a sleepable lock. If the entry * lock being acquired is already held (the trylock failed), VMRL_ERR_LOCK_ALREADY_HELD * is returned instead. * * @const VMRL_SH_NO_DESCEND_TRANSPARENT (sh streaming only) * Do not descend into transparent submaps. This may be useful for APIs * specifically wanting to examine the structure of the address space in the * kernel_map. * * @const VMRL_DESCEND_INTO_CONSTANT (ex, sh) * Whether iteration descends into constant submaps. * Descension into transparent submaps happens by default. * Exclusive locks: * - Can be made to descend into constant submap by passing * VMRL_EX_DESCEND_INTO_CONSTANT. Even though this an exclusive lock, there * are actually no locks taken in constant submaps. When descended into a * constant submap, the caller must not modify entries. * Shared locks: * - Can be made to descend into constant submaps by passing * VMRL_SH_DESCEND_INTO_CONSTANT */ __options_decl(vmrl_flags_t, uint32_t, { /* enumeration mode */ _VMRL_NO_HOLES = 0x00000001, _VMRL_ALLOW_HOLES = 0x00000002, _VMRL_STREAM_INTERNAL = 0x00000004, _VMRL_ATOMIC_INTERNAL = 0x00000008, _VMRL_MODE_MASK = 0x0000000f, VMRL_INVALID = 0x00000000, VMRL_STREAM = _VMRL_STREAM_INTERNAL | _VMRL_ALLOW_HOLES, VMRL_STREAM_NO_HOLES = _VMRL_STREAM_INTERNAL | _VMRL_NO_HOLES, VMRL_ATOMIC = _VMRL_ATOMIC_INTERNAL | _VMRL_NO_HOLES, VMRL_ATOMIC_ALLOW_HOLES = _VMRL_ATOMIC_INTERNAL | _VMRL_ALLOW_HOLES, /* exclusive / shared */ VMRL_SHARED = 0x00000010, VMRL_EXCLUSIVE = 0x00000020, /* entry stabilization */ VMRL_RESOLVE_COW_AND_OBJ = 0x00000040, VMRL_VMO_ALLOCATE = 0x00000080, VMRL_NO_PMAP_UNNEST = 0x00000100, /* other flags */ VMRL_WHOLE_MAP = 0x00001000, VMRL_NO_MIN_MAX_CHECK = 0x00002000, VMRL_SIMPLIFY = 0x00004000, VMRL_INTERRUPTIBLE = 0x00008000, VMRL_ILK_LOCKED = 0x00010000, VMRL_TRY_LOCK_ENTRY = 0x00020000, VMRL_NO_DESCEND_TRANSPARENT = 0x00040000, VMRL_DESCEND_INTO_CONSTANT = 0x00080000, /* * internal flags set by the implementation only * for the duration of a function call and never persisted. */ _VMRL_SETUP_COW = 0x01000000, _VMRL_SETUP_COW_NOCLIP = 0x02000000, _VMRL_PREPARE_FOR_SHARE_NOCLIP = 0x04000000, _VMRL_PREPARE_FOR_SHARE_WITH_UPL = 0x08000000, /* internal flags set by the implementation only */ _VMRL_SINGLE_ENTRY = 0x40000000, _VMRL_KERNEL_PMAP = 0x80000000, }); /*! * @typedef vmrl_ex_flags_t * * @brief * Flags affecting the behavior of acquiring an exclusive range lock. * * @discussion * The minimal behavior for the exclusive range lock is to: * - clip entries within the [start, end) range being asked, * - pmap unnest said entries if necessary. */ __options_decl(vmrl_ex_flags_t, uint32_t, { /* enumeration mode */ VMRL_EX_STREAM = VMRL_EXCLUSIVE | VMRL_STREAM, VMRL_EX_STREAM_NO_HOLES = VMRL_EXCLUSIVE | VMRL_STREAM_NO_HOLES, VMRL_EX_ATOMIC = VMRL_EXCLUSIVE | VMRL_ATOMIC, VMRL_EX_ATOMIC_ALLOW_HOLES = VMRL_EXCLUSIVE | VMRL_ATOMIC_ALLOW_HOLES, /* entry stabilization */ VMRL_EX_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ, VMRL_EX_NO_PMAP_UNNEST = VMRL_NO_PMAP_UNNEST, /* other flags */ VMRL_EX_WHOLE_MAP = VMRL_WHOLE_MAP, VMRL_EX_NO_MIN_MAX_CHECK = VMRL_NO_MIN_MAX_CHECK, VMRL_EX_SIMPLIFY = VMRL_SIMPLIFY, VMRL_EX_INTERRUPTIBLE = VMRL_INTERRUPTIBLE, VMRL_EX_ILK_LOCKED = VMRL_ILK_LOCKED, VMRL_EX_TRY_LOCK_ENTRY = VMRL_TRY_LOCK_ENTRY, VMRL_EX_DESCEND_INTO_CONSTANT = VMRL_DESCEND_INTO_CONSTANT, }); /*! * @typedef vmrl_sh_flags_t * * @brief * Flags affecting the behavior of acquiring a shared range lock. * * @discussion * The default behavior for the shared range lock is to be streaming, * (however that isn't true for a downgraded exclusive lock). */ __options_decl(vmrl_sh_flags_t, uint32_t, { /* enumeration mode */ VMRL_SH_STREAM = VMRL_SHARED | VMRL_STREAM, VMRL_SH_STREAM_NO_HOLES = VMRL_SHARED | VMRL_STREAM_NO_HOLES, VMRL_SH_ATOMIC = VMRL_SHARED | VMRL_ATOMIC, /* entry stabilization */ VMRL_SH_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ, VMRL_SH_VMO_ALLOCATE = VMRL_VMO_ALLOCATE, /* other flags */ VMRL_SH_WHOLE_MAP = VMRL_WHOLE_MAP, VMRL_SH_NO_MIN_MAX_CHECK = VMRL_NO_MIN_MAX_CHECK, VMRL_SH_INTERRUPTIBLE = VMRL_INTERRUPTIBLE, VMRL_SH_ILK_LOCKED = VMRL_ILK_LOCKED, VMRL_SH_TRY_LOCK_ENTRY = VMRL_TRY_LOCK_ENTRY, VMRL_SH_NO_DESCEND_TRANSPARENT = VMRL_NO_DESCEND_TRANSPARENT, VMRL_SH_DESCEND_INTO_CONSTANT = VMRL_DESCEND_INTO_CONSTANT, }); /*! * @typedef vmrl_find_ex_flags_t * * @brief * Flags affecting the behavior of acquiring an exclusive lock on an entry at or * after an address. */ __options_decl(vmrl_find_ex_flags_t, uint32_t, { VMRL_FIND_EX_DEFAULT = 0, /* entry stabilization */ VMRL_FIND_EX_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ, /* other flags */ VMRL_FIND_EX_NO_MIN_MAX_CHECK = VMRL_NO_MIN_MAX_CHECK, VMRL_FIND_EX_INTERRUPTIBLE = VMRL_INTERRUPTIBLE, VMRL_FIND_EX_ILK_LOCKED = VMRL_ILK_LOCKED, }); /*! * @typedef vmrl_find_sh_flags_t * * @brief * Flags affecting the behavior of acquiring a shared lock on an entry at or * after an address. */ __options_decl(vmrl_find_sh_flags_t, uint32_t, { VMRL_FIND_SH_DEFAULT = 0, /* entry stabilization */ VMRL_FIND_SH_RESOLVE_COW_AND_OBJ = VMRL_RESOLVE_COW_AND_OBJ, VMRL_FIND_SH_VMO_ALLOCATE = VMRL_VMO_ALLOCATE, /* other flags */ VMRL_FIND_SH_NO_MIN_MAX_CHECK = VMRL_NO_MIN_MAX_CHECK, VMRL_FIND_SH_INTERRUPTIBLE = VMRL_INTERRUPTIBLE, VMRL_FIND_SH_ILK_LOCKED = VMRL_ILK_LOCKED, VMRL_FIND_SH_NO_DESCEND_TRANSPARENT = VMRL_NO_DESCEND_TRANSPARENT, VMRL_FIND_SH_DESCEND_INTO_CONSTANT = VMRL_DESCEND_INTO_CONSTANT, }); /*! * @brief * The state of descent in submaps. */ __enum_decl(vmlc_descend_t, unsigned char, { VMLC_NOT_DESCENDED, VMLC_IN_TRANSPARENT_SUBMAP, VMLC_IN_CONSTANT_SUBMAP, }); /*! * @brief * These values should be used in conjunction with VMRL_X_WHOLE_MAP. * Their purpose is to alleviate the need from the user to specify the * exact limits of the first and last virtual addresses. */ #define VMRL_WHOLE_MAP_START (0) #define VMRL_WHOLE_MAP_END (0) /* The values are invalid VAs which are meant to fail the checks in * __vmrl_context_init() if left unmodified. */ static_assert(VMRL_WHOLE_MAP_START == VMRL_WHOLE_MAP_END); /*! * @brief * These values can be used in conjunction with VMRL_X_NO_OFFSET_CHECK * to iterate the map range from the start of the virtual address space * or to the end of the virtual address space. */ #define VMRL_START_VA(map) (0) #define VMRL_END_VA(map) ((vm_map_address_t)(-vm_map_page_size(map))) typedef struct vm_map_lock_ctx *vm_map_lock_ctx_t; typedef vm_map_lock_ctx_t vm_map_find_lock_ctx_t; typedef kern_return_t (^vm_map_lock_preflight_t)(vm_map_lock_ctx_t ctx, vm_map_entry_t entry); struct vm_map_lock_ctx { /* * Denotes the clip that must be applied to the entry range * relative to the current map. * In stream lock vmlc_req_start is updated on every iteration to the current start address */ mach_vm_address_t vmlc_req_start; mach_vm_address_t vmlc_req_end; /* public fields */ /*! * The map the current entry belongs to, * this might differ from the original map * passed to __vm_map_range_*_lock(). */ vm_map_t vmlc_map; /*! cursor state: the current vm entry */ vm_map_entry_t vmlc_vme; vm_map_lock_preflight_t vmlc_preflight; /* private fields */ /*! the flags passed to the __vm_map_range_*_lock() call */ vmrl_flags_t __vmlc_flags; /*! is a lock actively held */ bool __vmlc_locked : 1; /*! is this the first iteration */ bool __vmlc_first : 1; /*! Is the lock currently in a submap */ vmlc_descend_t __vmlc_descended : 2; uint32_t __vmlc_unused1 : 28; union { /*! This structure is used for VMRL_STREAM contexts */ struct { /* * The last address that's been processed (or the start * of the range, if we've just started) */ mach_vm_address_t last_processed_addr; kern_return_t first_error; uint32_t __vmlc_unused2; } __vmlc_streaming; /*! This structure is used for VMRL_ATOMIC contexts */ struct { vm_map_entry_t first_entry; mach_vm_address_t locked_range_start; mach_vm_address_t locked_range_end; } __vmlc_atomic; }; /* * If we are descended in a submap, store * start/end/map of the parent map */ vm_map_address_t __original_req_start; vm_map_address_t __original_req_end; vm_map_t __original_map; vm_map_offset_t __parent_offset; /* 0 if not descended */ vm_map_entry_t __parent_entry; }; #pragma mark flags acessors __attribute__((overloadable, always_inline, const)) static inline vmrl_flags_t __vmrl_flags(vmrl_flags_t flags) { return flags; } __attribute__((overloadable, always_inline)) static inline vmrl_flags_t __vmrl_flags(vm_map_lock_ctx_t ctx) { return ctx->__vmlc_flags; } __attribute__((overloadable, always_inline, const)) static inline vmrl_flags_t __vmrl_flags(vmrl_sh_flags_t flags) { return (vmrl_flags_t)flags; } __attribute__((overloadable, always_inline, const)) static inline vmrl_flags_t __vmrl_flags(vmrl_ex_flags_t flags) { return (vmrl_flags_t)flags; } __attribute__((overloadable, always_inline, const)) static inline vmrl_flags_t __vmrl_flags(vmrl_find_sh_flags_t flags) { return (vmrl_flags_t)flags; } __attribute__((overloadable, always_inline, const)) static inline vmrl_flags_t __vmrl_flags(vmrl_find_ex_flags_t flags) { return (vmrl_flags_t)flags; } /*! * @brief * Returns whether the specified flags or context denote a shared lock. */ #define vmrl_is_shared(x) \ ((bool)((__vmrl_flags(x) & (VMRL_SHARED | VMRL_EXCLUSIVE)) == VMRL_SHARED)) /*! * @brief * Returns whether the specified flags or context denote an exclusive lock. */ #define vmrl_is_exclusive(x) \ ((bool)((__vmrl_flags(x) & (VMRL_SHARED | VMRL_EXCLUSIVE)) == VMRL_EXCLUSIVE)) /*! * @brief * Denotes whether the specified context is for a kernel map. */ #define vmrl_is_kernel_pmap(x) \ ((bool)(__vmrl_flags(x) & _VMRL_KERNEL_PMAP)) /*! * @brief * Returns the mode of the lock (invalid, streaming, stream-no-holes, * atomic, or atomic-allow-holes). */ #define vmrl_mode(x) \ (__vmrl_flags(x) & _VMRL_MODE_MASK) /*! * @brief * Returns whether the specified flags or context denote a lock in streaming * mode. */ #define vmrl_is_streaming(x) \ ((bool)(vmrl_mode(x) & (_VMRL_STREAM_INTERNAL))) /*! * @brief * Returns whether the specified flags or context denote a lock in atomic * mode. */ #define vmrl_is_atomic(x) \ ((bool)(vmrl_mode(x) & (_VMRL_ATOMIC_INTERNAL))) /*! * @brief * Computes the wait flags (interruptible or not) for the specified flags or * context. */ #define vmrl_wait_interrupt(x) \ ((__vmrl_flags(x) & VMRL_INTERRUPTIBLE) ? THREAD_ABORTSAFE : THREAD_UNINT) #pragma mark lock context /*! * @brief * Static initializer for map range lock contexts. */ #define VM_MAP_LOCK_CTX_INITIALIZER { } /* * @function vm_map_lock_ctx_init() * * @brief * Initialize a map lock context to an empty state ready to be used for locking. */ static inline void vm_map_lock_ctx_init(vm_map_lock_ctx_t vml_ctx) { *vml_ctx = (struct vm_map_lock_ctx)VM_MAP_LOCK_CTX_INITIALIZER; } /* * @function assert_vm_map_lock_ctx_unlocked() * * @brief * Helper function to assert that a lock is unlocked. * * @discussion * This should not be called directly, * and is called internally by VM_MAP_LOCK_CTX_DECLARE. */ static inline void assert_vm_map_lock_ctx_unlocked(vm_map_lock_ctx_t vml_ctx __assert_only) { assert(!vml_ctx->__vmlc_locked); } /*! * @brief * Declare a lock context called @c name on the stack. * * @discussion * The context can be used for serveral lock acquisition in a row, * but the lock must be unlocked on any exit path from the function. */ #define VM_MAP_LOCK_CTX_DECLARE(name) \ __attribute__((cleanup(assert_vm_map_lock_ctx_unlocked))) \ struct vm_map_lock_ctx private_##name = VM_MAP_LOCK_CTX_INITIALIZER; \ const vm_map_lock_ctx_t name = &private_##name /*! * @brief * Declare a lock context called @c name on the stack for finding a single entry * at (or after) an address */ #define VM_MAP_FIND_LOCK_CTX_DECLARE(name) \ __attribute__((cleanup(assert_vm_map_lock_ctx_unlocked))) \ struct vm_map_lock_ctx private_##name = VM_MAP_LOCK_CTX_INITIALIZER; \ const vm_map_find_lock_ctx_t name = &private_##name /*! * @brief * Sets up the preflight hook in a context. * * @discussion * The preflight hook allows for lockers to add custom behaviors and validation * as the lock is being taken. * * For atomic locks, the preflight outcome will be returned by * @c vm_map_range_*_lock(), for streaming locks it is returned via the error * parameter of the @c vm_map_range_next_with_error() family of functions. * For atomic locks, the preflight is called on every entry during the process * of locking the range. * For streaming locks, the preflight is called on each entry at advance time. * * In all cases (incl. transparent submaps or constant submaps with * VMRL_DESCEND_INTO_CONSTANT), the preflight hook gets called on any entry that * is being iterated. * * The preflight hook may be called multiple times on the same entry. * * Preflight hooks are called with the entry locked exclusively for exclusive * locks, and at least shared for shared locks. * * Preflights are run with the interlock held. The intent is for them to make * policy checks by reading fields of the entry, or fields of the associated * object that are safe to access without the object lock. They should not * modify the state of the VM. They should ideally run quickly and not perform * any blocking operations such as acquiring an object lock. * * The preflight hook can return the following values: * * - KERN_SUCCESS The entry is accepted. * * - VMRL_ERR_SKIP_PREPARE * The entry should not be "prepared" (no unnesting, * no clipping, etc...). * * This error doesn't stop enumeration and is swallowed, * and during the enumeration, it is the responsibility * of the client to notice this entry has not been * prepared and shouldn't be mutated. * * This error is processed internally by the lock and will * not be returned to clients of the @c vm_map_range_*_lock() * or @c vm_map_range_next_with_error() APIs. * * - VMRL_ERR_WAIT_FOR_KUNWIRE * The caller desires waiting for the wire count of the * entry to drop. * * This error doesn't stop enumeration, but the preflight * hook will be called on this entry again once the * kernel wire count condition changed. It will also cause * @c vm_map_range_*_lock() or @c vm_map_range_next_with_error() * to hang until the wire count drops. * * This error is processed internally by the lock and will * not be returned to clients of the @c vm_map_range_*_lock() * or @c vm_map_range_next_with_error() APIs. * * - VMRL_ERR_SETUP_SYMMETRIC_COW * The caller requests to setup COW on the entry. * If the lock is a shared lock, this will temporarily * take an exclusive lock on the entry. * * - VMRL_ERR_SETUP_SYMMETRIC_COW_NOCLIP * Same as VMRL_ERR_SETUP_SYMMETRIC_COW but entries are not clipped * to the requested range. * This serves to preserve the behaviour of APIs that have not * clipped before the range-lock change. * clipping behaviour is an internal implementation detail, except * in the case of mach_make_memory_entry() where the effects of * clipping is exposed to the user due to use of * vmk_flags.vmkf_copy_single_object. These two _NOCLIP flags * cater to this case. * * - VMRL_ERR_PREPARE_FOR_SHARE_NOCLIP * The caller indicates that the entry is going to be shared * with another map, but that they do not want the entry to * be clipped. The lock stabilizes the object for the entry * and sets is_shared accordingly. * If the entry is locked shared it will temporarily upgrade to exclusive * Entries are not clipped to the requested range, see above comment. * * - VMRL_ERR_PREPARE_FOR_SHARE_WITH_UPL * The caller indicates that the entry is going to be shared, * and that it may be clipped. * The lock stabilizes the object for the entry but does not * set is_shared. * If the entry is locked shared it will temporarily upgrade to exclusive * * - other An error that stops the enumeration and will be bubbled * up to callers. */ static inline void vm_map_lock_ctx_set_preflight( vm_map_lock_ctx_t vml_ctx, vm_map_lock_preflight_t preflight) { assert_vm_map_lock_ctx_unlocked(vml_ctx); vml_ctx->vmlc_preflight = preflight; } /*! * @brief * Converts a parent map address to be relative to the currently * iterated map (possibly a submap). */ static inline vm_map_offset_t vm_map_lock_ctx_from_parent_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr) { return addr - ctx->__parent_offset; } /*! * @brief * Converts an address relative to the currently iterated map to be relative * to the parent map. */ static inline vm_map_offset_t vm_map_lock_ctx_to_parent_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr) { return addr + ctx->__parent_offset; } /*! * @brief * Returns the entry offset for a given address, relative to the currently * iterated map (possibly a submap). */ static inline vm_map_offset_t vm_map_lock_ctx_offset_for_address(vm_map_lock_ctx_t ctx, vm_map_address_t addr) { vm_map_entry_t vme = ctx->vmlc_vme; /* * the address really should be within the entry, * and within the enumeration range. * (<= because they may want to know about the end addr) */ assert(ctx->vmlc_req_start <= addr && addr <= ctx->vmlc_req_end); assert(vme->vme_start <= addr && addr <= vme->vme_end); return VME_OFFSET(vme) + addr - vme->vme_start; } /*! * @brief * Returns the entry offset for a given address relative to the parent map * that is currerntly iterated. */ static inline vm_map_offset_t vm_map_lock_ctx_offset_for_parent_address( vm_map_lock_ctx_t ctx, vm_map_address_t addr) { addr = vm_map_lock_ctx_from_parent_address(ctx, addr); return vm_map_lock_ctx_offset_for_address(ctx, addr); } /*! * @brief * Return the bounds of the current entry, relative to the currently * iterated map (possibly a submap). * * @discussion * This function will take the requested bounds into account, * and present the real bounds of the entry that the caller should consider. * * @param startp (optional out parameter) the entry start. * @param endp (optional out parameter) the entry end. * @param sizep (optional out parameter) the entry size. */ static inline void vm_map_lock_ctx_bounds( vm_map_lock_ctx_t ctx, vm_map_address_t *startp, vm_map_address_t *endp, vm_map_size_t *sizep) { vm_map_entry_t vme = ctx->vmlc_vme; vm_map_address_t start = MAX(vme->vme_start, ctx->vmlc_req_start); vm_map_address_t end = MIN(vme->vme_end, ctx->vmlc_req_end); if (startp) { *startp = start; } if (endp) { *endp = end; } if (sizep) { *sizep = end - start; } } /*! * @brief * Return the bounds of the current entry, relative to the currently * iterated parent map, regardless of whether the iteration descended * into a submap. * * @discussion * This function will take the requested bounds into account, * and present the real bounds of the entry that the caller should consider. * * @param startp (optional out parameter) the entry start. * @param endp (optional out parameter) the entry end. * @param sizep (optional out parameter) the entry size. */ static inline void vm_map_lock_ctx_bounds_in_parent( vm_map_lock_ctx_t ctx, vm_map_address_t *startp, vm_map_address_t *endp, vm_map_size_t *sizep) { vm_map_lock_ctx_bounds(ctx, startp, endp, sizep); if (startp) { *startp = vm_map_lock_ctx_to_parent_address(ctx, *startp); } if (endp) { *endp = vm_map_lock_ctx_to_parent_address(ctx, *endp); } } /*! * @brief * Return the offsets bounds of the current entry's object or submap. * * @discussion * This function will take the requested bounds into account, * and present the real offsets bounds of the object or submap * that the caller should consider. * * @param startp (optional out parameter) the object/submap start. * @param endp (optional out parameter) the object/submap end. * @param sizep (optional out parameter) the object/submap size. */ static inline void vm_map_lock_ctx_offset_bounds( vm_map_lock_ctx_t ctx, vm_object_offset_t *startp, vm_object_offset_t *endp, vm_object_size_t *sizep) { vm_map_lock_ctx_bounds(ctx, startp, endp, sizep); if (startp) { *startp = vm_map_lock_ctx_offset_for_address(ctx, *startp); } if (endp) { *endp = vm_map_lock_ctx_offset_for_address(ctx, *endp); } } #pragma mark lock / unlock /*! * @function vm_map_range_ex_lock() * * @discussion * This function acquires an exclusive range lock for the [start, end) range * within the @c map VM map. * * This function initiates the context cursor to the first entry * in the range. * * Exclusive locks descend into transparent submaps by default and do not * descend into constant submaps. * * @param vml_ctx the locking context to use. * @param map the map being locked. * This parameter will be NULLed on success to prevent it * accidentally being used incorrectly by client code, which * should instead use vm_map_lock_ctx_get_map() * @param start the beginning of the range to lock * @param end the end of the range to lock * @param flags a set of flags altering the lock behavior * * @returns * - KERN_SUCCESS the lock was acquired * * - KERN_ABORTED the lock wasn't acquired because of the sleep operation * being interrupted (VMRL_SH_ATOMIC & VMRL_EX_INTERRUPTIBLE only). * * - KERN_INVALID_ADDRESS * the lock wasn't acquired because no entry was found * within [start, end) (impossible for * VMRL_EX_ATOMIC_ALLOW_HOLES). * * - KERN_INVALID_ADDRESS * the lock wasn't acquired because there was a gap within * [start, end) (only for VMRL_EX_ATOMIC or * VMRL_EX_STREAM_NO_HOLES). * * - VMRL_ERR_LOCK_ALREADY_HELD * the lock wasn't acquired because the entry was already * locked. (VMRL_SH_ATOMIC & VMRL_EX_TRY_LOCK_ENTRY only) * * - other the lock wasn't acquired because preparing the entry * failed with that error (entry stabilization only). * * - any error the lock wasn't acquired because its preflight * rejected it (vmlc_preflight being set and * VMRL_EX_ATOMIC only). */ __result_use_check extern kern_return_t vm_map_range_ex_lock( vm_map_lock_ctx_t vml_ctx, vm_map_t *map, vm_map_address_t start, vm_map_address_t end, vmrl_ex_flags_t flags) __attribute__((__diagnose_if__((flags & _VMRL_MODE_MASK) == VMRL_INVALID, "no mode selected", "error"))); /*! * @function vm_map_range_sh_lock() * * @discussion * This function acquires a shared range lock for the [start, end) range * within the @c map VM map. * * This function initiates the context cursor to the first entry * in the range. * * @param vml_ctx the locking context to use. * @param map the map being locked. * This parameter will be NULLed on success to prevent it * accidentally being used incorrectly by client code, which * should instead use vm_map_lock_ctx_get_map() * @param start the beginning of the range to lock * @param end the end of the range to lock * @param flags a set of flags altering the lock behavior * (VMRL_STREAM is implied) * * @returns * - KERN_SUCCESS the lock was acquired * * - KERN_ABORTED the lock wasn't acquired because of the sleep operation * being interrupted (VMRL_SH_ATOMIC & VMRL_SH_INTERRUPTIBLE only). * * - KERN_INVALID_ADDRESS * the lock wasn't acquired because no entry was found * within [start, end). * * - KERN_INVALID_ADDRESS * the lock wasn't acquired because there was a gap within * [start, end) (only for VMRL_SH_ATOMIC or * VMRL_SH_STREAM_NO_HOLES). * * - VMRL_ERR_LOCK_ALREADY_HELD * the lock wasn't acquired because the entry was already * locked. (VMRL_SH_ATOMIC & VMRL_SH_TRY_LOCK_ENTRY only) * * - other the lock wasn't acquired because preparing the entry * failed with that error (entry stabilization only). * * - other the lock wasn't acquired because its preflight * rejected it (vmlc_preflight being set only). */ __result_use_check extern kern_return_t vm_map_range_sh_lock( vm_map_lock_ctx_t vml_ctx, vm_map_t *map, vm_map_address_t start, vm_map_address_t end, vmrl_sh_flags_t flags) __attribute__((__diagnose_if__((flags & _VMRL_MODE_MASK) == VMRL_INVALID, "no mode selected", "error"))); /*! * @function vm_map_range_ex_to_sh() * * @brief * Downgrades an atomic exclusive held range lock * to an atomic shared range lock. * * @discussion * Exclusive range locks can only be downgraded if: * - VMRL_EX_STREAM wasn't passed to @c vm_map_range_ex_lock(). * * A downgrade will always happen atomically, meaning that it is * guaranteed that no other exclusive locks can come in between * the exclusive to shared transition * * Once downgraded, the shared range lock is _not_ in streaming * mode. This nuance is hidden behind the cursor/iterator API. * * Downgrades should only happen after entirely iterating an exclusive range. * They reset the cursor to the beginning of that range. * * @param vml_ctx the context passed to @c vm_map_range_ex_lock() */ extern void vm_map_range_ex_to_sh( vm_map_lock_ctx_t vml_ctx); /*! * @function vm_map_lock_ctx_from_locked_entries() * * @brief * Configures a context to manage a series of already-exclusive-locked entries. * * @discussion * Once configured, the lock context is indistinguishable from the hypothetical * context that @c vm_map_range_ex_lock() would have configured had it been * used to atomically lock the same range. * * @param vml_ctx the context to be initialized * @param map the map containing the locked entries * @param start the first address in the already-exclusive-locked range * @param size the total size of the range mapped by the locked entries */ extern kern_return_t vm_map_lock_ctx_from_locked_entries( vm_map_lock_ctx_t vml_ctx, vm_map_t *map, vm_map_address_t start, vm_map_size_t size); /*! * @function vm_map_range_ex_unlock() * * @brief * Releases an exclusive range lock successfully acquired * by @c vm_map_range_ex_lock(). * * @param vml_ctx the context passed to @c vm_map_range_ex_lock() * @param map the original map passed to the lock call. * will be written into *map. This should be the original map * passed to the lock call. It can also be NULL. */ extern void vm_map_range_ex_unlock( vm_map_lock_ctx_t vml_ctx, vm_map_t *map); /*! * @function vm_map_range_sh_unlock() * * @brief * Releases a shared range lock successfully acquired * by @c vm_map_range_sh_lock(). * * @param vml_ctx the context passed to @c vm_map_range_sh_lock() * @param map the original map passed to the lock call. * will be written into *map. This should be the original map * passed to the lock call. It can also be NULL. */ extern void vm_map_range_sh_unlock( vm_map_lock_ctx_t vml_ctx, vm_map_t *map); #pragma mark range iteration /*! * @function vm_map_range_atomic_next() * * @abstract * Advance the cursor of the lock context. * * @discussion * This function updates the @c vmlc_vme field of the context * to the value it returns, locked shared or exclusive according * to how the lock was set up. * * @param vml_ctx the locking context to use. */ extern vm_map_entry_t vm_map_range_atomic_next( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_atomic_peek * * @abstract * Returns the same entry that vm_map_range_atomic_next() would, but does not * advance the cursor of the lock context. * * @discussion * Unlike @c vm_map_range_atomic_next(), a successful call to this function does * not have any side effects, and may peek into an empty lock context. * * @param vml_ctx the locking context to use. */ extern vm_map_entry_t vm_map_range_atomic_peek( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_ex_atomic_pop() * * @abstract * Advance the cursor of the lock context, and remove it from the range lock. * * @discussion * Unlike @c vm_map_range_next(), this function doesn't set * the @c vmlc_vme field of the context. * * Instead, it "removes" the current entry from the range lock and donates * it to the client, for the client to unlock. This is useful for interfaces * that remove entries from the map, and would confuse the range lock. * * The responsibility remains on the caller to remove the entry from the map, * this function only removes it from the range lock. * * Calls to @c vm_map_range_ex_atomic_pop should not be intermixed with calls to * @c vm_map_range_next or its variants. * * Note that calling @c vm_map_range_*_unlock() is still mandatory. * * @param vml_ctx the locking context to use. */ extern vm_map_entry_t vm_map_range_ex_atomic_pop( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_atomic_reset() * * @brief * resets the iteration cursor of a range locked in atomic mode. * * @discussion * This is invalid for streaming locks. * It will panic due to assertions if called on a streaming lock. * * @param vml_ctx the context passed to @c vm_map_range_ex_lock() */ extern void vm_map_range_atomic_reset( vm_map_lock_ctx_t vml_ctx); /*! * @function vm_map_range_stream_next_with_error() * * @abstract * Advance the cursor of the lock context. * * @discussion * This function updates the @c vmlc_vme field of the context * to the value it returns, locked shared or exclusive according * to how the lock was set up. * * @param vml_ctx * the locking context to use. * * @param kr * If a non NULL vm map entry is returned, then `kr` will always be * KERN_SUCCCES. * * Otherwise, the errors that can be returned are: * * - KERN_SUCCESS a valid entry was returned, * or the enumeration finished successfully. * * - KERN_ABORTED the range advance failed because some locking operation * was interrupted (VMRL_*_INTERRUPTIBLE only). * * - KERN_INVALID_ADDRESS * the range advance found a hole and stopped the iteration * (VMRL_STREAM_NO_HOLES only). * * - VMRL_ERR_LOCK_ALREADY_HELD * the lock wasn't acquired because the entry was already * locked. (VMRL_TRY_LOCK_ENTRY only) * * - other the range advance failed because some entry preparation * failed with that error (VMRL_*_RESOLVE only). * * - other the lock wasn't acquired because its preflight * rejected it (vmlc_preflight set only). */ extern vm_map_entry_t vm_map_range_stream_next_with_error( vm_map_lock_ctx_t vml_ctx, kern_return_t *kr __attribute__((nonnull))) __result_use_check; /*! * @function vm_map_range_stream_next() * * @abstract * Advance the cursor of the lock context. * * @discussion * This function is a simpler version of @c vm_map_range_next(), * for cases when the caller knows it will never return an error. * If it would have, this function will panic. * * @param vml_ctx * the locking context to use. * */ extern vm_map_entry_t vm_map_range_stream_next( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_ex_stream_pop_with_error() * * @abstract * Advance the cursor of the lock context, and remove it from the range lock. * * @discussion * Unlike @c vm_map_range_stream_next(), this function doesn't set * the @c vmlc_vme field of the context. * * Instead, it "removes" the current entry from the range lock and donates * it to the client, for the client to unlock. This is useful for interfaces * that remove entries from the map, and would confuse the range lock. * * The responsibility remains on the caller to remove the entry from the map, * this function only removes it from the range lock. * * Note that calling @c vm_map_range_*_unlock() is still mandatory. * * @param vml_ctx the locking context to use. */ extern vm_map_entry_t vm_map_range_ex_stream_pop_with_error( vm_map_lock_ctx_t vml_ctx, kern_return_t *kr __attribute__((nonnull))) __result_use_check; /*! * @function vm_map_range_ex_stream_pop() * * @abstract * Advance the cursor of the lock context, and remove it from the range lock. * * @discussion * This is the same as vm_map_range_ex_stream_pop() * for cases when the caller knows that no error will be returned. * If it would have, this function will panic. */ extern vm_map_entry_t vm_map_range_ex_stream_pop( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_found_entry_ex_pop_curr() * * @brief * Remove an entry locked via @c vm_map_find_entry_ex_locked{,_or_next} from * the range lock. The entry remains locked, and it becomes the caller's * responsibility to unlock the entry. * * @param vml_ctx the context passed to * @c vm_map_find_entry_ex_locked{,_or_next}(). */ extern void vm_map_found_entry_ex_pop_curr( vm_map_find_lock_ctx_t vml_ctx); /*! * @function vm_map_range_stream_drop() * * @discussion * Allows clients of streaming locks to drop the lock on their current entry * (typically so they can perform slow, object-level operations) without * advancing to the next entry. * * Subsequent calls to @c vm_map_range_stream_next() are still permitted. * The next call to @c vm_map_range_stream_next() will advance to the entry * following the end of the entry being unlocked by this call. * * @param vml_ctx the locking context to use. */ extern void vm_map_range_stream_drop( vm_map_lock_ctx_t vml_ctx); /*! * @function vm_map_range_stream_drop_without_advance() * * @discussion * Similar to @c vm_map_range_stream_drop, but the address the lock is * processing is not advanced to the end of the current entry. * Instead, the next call to @c vm_map_range_stream_next will lock the entry at * the start of the current iteration's lock bounds. * This function is used if you need to drop an entry lock for the same range - * it's generally uncommon but is used in faulting-like patterns sometimes. * * @param vml_ctx the locking context to use. */ extern void vm_map_range_stream_drop_without_advance( vm_map_lock_ctx_t vml_ctx); /*! * @function vm_map_range_next_with_error() * * @abstract * Advance the cursor of the lock context. * * @discussion * This function updates the @c vmlc_vme field of the context * to the value it returns, locked shared or exclusive according * to how the lock was set up. * * Note: when the caller knows which kind of lock has been setup, * using @c vm_map_range_atomic_next() or * @c vm_map_range_stream_next() directly is preferred. * * @see @c vm_map_range_stream_next_with_error(). * @see @c vm_map_range_atomic_next(). * * @param vml_ctx the locking context to use. * @param kr an out parameter that contains an error. * @see vm_map_range_stream_next(). */ extern vm_map_entry_t vm_map_range_next_with_error( vm_map_lock_ctx_t vml_ctx, kern_return_t *kr __attribute__((nonnull))) __result_use_check; /*! * @function vm_map_range_next() * * @abstract * Advance the cursor of the lock context. * * @discussion * This is the same as vm_map_range_next_with_error() * for cases when the caller knows that no error will be returned. * If it would have, this function will panic. */ extern vm_map_entry_t vm_map_range_next( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_ex_pop_with_error() * * @abstract * Advance the cursor of the lock context, and remove it from the range lock. * * @discussion * Note: when the caller knows which kind of lock has been setup, * using @c vm_map_range_ex_atomic_pop() or * @c vm_map_range_ex_stream_pop() directly is preferred. * * @see @c vm_map_range_ex_stream_pop(). * @see @c vm_map_range_ex_atomic_pop(). * * @param vml_ctx the locking context to use. * @param kr an out parameter that contains an error. * @see vm_map_range_stream_next(). */ extern vm_map_entry_t vm_map_range_ex_pop_with_error( vm_map_lock_ctx_t vml_ctx, kern_return_t *kr __attribute__((nonnull))) __result_use_check; /*! * @function vm_map_range_ex_pop() * * @abstract * Advance the cursor of the lock context, and remove it from the range lock. * * @discussion * This is the same as vm_map_range_ex_pop_with_error() * for cases when the caller knows that no error will be returned. * If it would have, this function will panic. */ extern vm_map_entry_t vm_map_range_ex_pop( vm_map_lock_ctx_t vml_ctx) __result_use_check; /*! * @function vm_map_range_lock_clip_end * * @discussion * Splits a locked entry at @c endaddr. * * The entry passed in will be set to end at the given address, * and copy of the entry inserted after it, remaining locked. * If the address requested is after the entry, this function does nothing. * * The map should be unlocked. * * The entry must be owned exclusively by the caller. * * pmap unnesting must have happened prior * * @param ctx the lock context * @param entry the entry to be clipped * @param endaddr the address to clip the entry to */ extern void vm_map_range_lock_clip_end( vm_map_lock_ctx_t ctx, vm_map_entry_t entry, vm_map_offset_t endaddr); /*! * @function vm_map_range_lock_clip_start * * @discussion * Clips an entry to start at a greater start address. * * The entry passed in will be set to start at the given address, and a copy * of the entry will be inserted before it. * If the address requested is after the entry, this function does nothing. * * The map should be unlocked. * * The entry must be owned exclusively by the caller. * * pmap unnesting must have happened prior * * @param ctx the lock context * @param entry the entry to be clipped * @param startaddr the address to clip the entry to */ extern void vm_map_range_lock_clip_start( vm_map_lock_ctx_t ctx, vm_map_entry_t entry, vm_map_offset_t startaddr); /*! * @function vm_map_found_entry_clip_end_ilocked * * @discussion * Clips an entry locked via @c vm_map_find_entry_sh_locked{,_or_next} to end * at a lesser end address. * * The entry passed in will be set to end at the given address, * and a copy of the entry will be inserted after it. * * The entry must be owned exclusively by the caller. * The address requested to clip to must be less than the current end of the entry. * * Requires the original entry and interlock to be exclusively locked. * Returns with the original entry, interlock, and newly-created entry * exclusively locked. * * Since a "found entry" context necessarily contains only a single entry, * the new entry is NOT a part of the lock context and it is the caller's * responsibility to handle the new entry appropriately. * * pmap unnesting must have happened prior * * @param ctx the context passed to vm_map_find_entry_sh_locked{,_or_next} * @param endaddr the address to clip the entry to * * @returns a pointer to the newly created and locked entry */ extern vm_map_entry_t vm_map_found_entry_clip_end_ilocked( vm_map_find_lock_ctx_t ctx, vm_map_offset_t endaddr); /* * @function vm_map_entry_lock_resolve_symmetric_cow * * @discussion * Resolve symmetric CoW for an entry with needs_copy. The entry must * have needs_copy. */ extern void vm_map_entry_lock_resolve_symmetric_cow( vm_map_t map, vm_map_entry_t entry); /* * @function vm_map_entry_lock_allocate_object * * @discussion * Allocate a vm_object for an entry with no vm_object. */ extern void vm_map_entry_lock_allocate_object( vm_map_entry_t entry, vm_map_serial_t provenance); #pragma mark Locking one entry at (or after) a fixed address (aka "Single Entry" lock) /*! * @function vm_map_find_entry_sh_locked * * @brief * Share-lock a single entry at a given address. * * @discussion * If an entry exists such that * @c entry->vme_start <= @c addr < @c entry->vme_end * is is locked, in shared mode. * * @param vml_ctx the locking context to use. * @param map the map being locked. * This parameter will be NULLed on success to prevent it * accidentally being used incorrectly by client code, which * should instead use vm_map_lock_ctx_get_map() * @param addr the address at which to look for an entry. * @param flags a set of flags altering the lock behavior * (VMRL_SHARED is implied). * * @returns * - KERN_SUCCESS * if an entry was successfully found and locked. The entry * can be found at @c vml_ctx->vmlc_vme. * - KERN_INVALID_ADDRESS * if no entry exists at @c addr in @c map. * - KERN_ABORTED * if the lock wasn't acquired because of the sleep * operation being interrupted (VMRL_SH_INTERRUPTIBLE * only). * - other * any error returned by the preflight hook. * * If any error is returned, vml_ctx public fields are not set and no lock is * acquired. */ __result_use_check extern kern_return_t vm_map_find_entry_sh_locked( vm_map_find_lock_ctx_t vml_ctx, vm_map_t *map, vm_map_address_t addr, vmrl_find_sh_flags_t flags); /*! * @function vm_map_find_entry_ex_locked * * @brief * Exclusively lock a single entry at a given address. * * @discussion * If an entry exists such that * @c entry->vme_start <= @c addr < @c entry->vme_end * it is exclusively locked. * * @param vml_ctx the locking context to use. * @param map the map being locked. * This parameter will be NULLed on success to prevent it * accidentally being used incorrectly by client code, which * should instead use vm_map_lock_ctx_get_map() * @param addr the address at which to look for an entry. * @param flags a set of flags altering the lock behavior * (VMRL_EXCLUSIVE is implied). * * @returns * - KERN_SUCCESS * if an entry was successfully found and locked. The entry * can be found at @c vml_ctx->vmlc_vme. * - KERN_INVALID_ADDRESS * if no entry exists at @c addr in @c map. * - KERN_ABORTED * if the lock wasn't acquired because of the sleep * operation being interrupted (VMRL_EX_INTERRUPTIBLE * only). * - other * any error returned by the preflight hook. * * If any error is returned, vml_ctx public fields are not set and no lock is * acquired. */ __result_use_check extern kern_return_t vm_map_find_entry_ex_locked( vm_map_find_lock_ctx_t vml_ctx, vm_map_t *map, vm_map_address_t addr, vmrl_find_ex_flags_t flags); /*! * @function vm_map_found_entry_sh_unlock * * @brief * Unlock an entry locked via @c vm_map_find_entry_sh_locked{,_or_next}. * * @param vml_ctx the context passed to * @c vm_map_find_entry_sh_locked{,_or_next}(). * @param map the original map passed to the lock call. * will be written into *map. This should be the original map * passed to the lock call. It can also be NULL. */ extern void vm_map_found_entry_sh_unlock( vm_map_find_lock_ctx_t vml_ctx, vm_map_t *map); /*! * @function vm_map_found_entry_ex_unlock * * @brief * Unlock an entry locked via @c vm_map_find_entry_ex_locked{,_or_next}. * * @param vml_ctx the context passed to * @c vm_map_find_entry_ex_locked{,_or_next}(). * @param map the original map passed to the lock call. * will be written into *map. This should be the original map * passed to the lock call. It can also be NULL. */ extern void vm_map_found_entry_ex_unlock( vm_map_find_lock_ctx_t vml_ctx, vm_map_t *map); /*! * @function vm_map_lock_ctx_is_descended * * @brief * Return if a lock context is descended into a submap */ static inline bool vm_map_lock_ctx_is_descended(vm_map_lock_ctx_t ctx) { return ctx->__vmlc_descended != VMLC_NOT_DESCENDED; } /*! * @function vm_map_lock_ctx_in_constant_submap * * @brief * Return if a lock context is descended into a constant submap */ static inline bool vm_map_lock_ctx_in_constant_submap(vm_map_lock_ctx_t ctx) { return ctx->__vmlc_descended == VMLC_IN_CONSTANT_SUBMAP; } /*! * @function vm_map_lock_ctx_get_map * * @brief * Returns the current vm_map_t the iteration is at * This may be different from the map the lock was called on * in case the lock is descended into a submap. */ static inline vm_map_t vm_map_lock_ctx_get_map(vm_map_lock_ctx_t ctx) { return ctx->vmlc_map; } /*! * @function vm_map_found_entry_get_entry * * @brief * For a find entry lock, return the currently locked entry. * The ctx must be locked. */ static inline vm_map_entry_t vm_map_found_entry_get_entry(vm_map_find_lock_ctx_t ctx) { assert(ctx->__vmlc_locked); return ctx->vmlc_vme; } /*! * @function vm_map_lock_ctx_is_in_needs_copy_submap * * @brief * Return whether the lock context is currently descended into a * needs_copy submap. */ static inline bool vm_map_lock_ctx_is_in_needs_copy_submap(vm_map_lock_ctx_t ctx) { if (vm_map_lock_ctx_is_descended(ctx)) { return ctx->__parent_entry->needs_copy; } return false; } /*! * @function vm_map_lock_ctx_is_in_pmap_nested_submap * * @brief * Return whether the lock context is currently descended into a submap with * a nested pmap. * See the comment in @vm_range_lock_pmap_unnest_and_clip for more info about * what that means. */ static inline bool vm_map_lock_ctx_is_in_pmap_nested_submap(vm_map_lock_ctx_t ctx) { if (vm_map_lock_ctx_is_descended(ctx)) { return ctx->__parent_entry->use_pmap; } return false; } /* * @function vm_map_lock_ctx_get_parent_entry_window * * @brief * This function should likely only be used for vm_map_region_recurse. * It gives the window that the parent entry gives into the submap, in * the address coordinates of the submap. * * *IMPORTANT* * @warning * This is regardless of the bounds the lock ctx asked for. That means these * bounds may be before/after the requested bounds of the lock ctx * (vmlc_req_start/end). * That's because this is what vm_map_region_recurse needs, but it is unlikely * any other functions would want to go backwards from the range requested. */ static inline void vm_map_lock_ctx_get_parent_entry_window( vm_map_lock_ctx_t ctx, vm_map_address_t *startp, vm_map_address_t *endp) { assert(vm_map_lock_ctx_is_descended(ctx)); *startp = vm_map_lock_ctx_from_parent_address(ctx, ctx->__parent_entry->vme_start); *endp = vm_map_lock_ctx_from_parent_address(ctx, ctx->__parent_entry->vme_end); } /*! * @function vm_map_range_ex_lock_add_flags * * @brief * Add flags to an exclusive vm_map_range lock. */ extern void vm_map_range_ex_lock_add_flags( vm_map_lock_ctx_t vml_ctx, vmrl_ex_flags_t flags); /*! * @function vm_map_range_ex_lock_remove_flags * * @brief * Remove flags from an exclusive vm_map_range lock. */ extern void vm_map_range_ex_lock_remove_flags( vm_map_lock_ctx_t vml_ctx, vmrl_ex_flags_t flags); /*! * @function vm_map_range_sh_lock_add_flags * * @brief * Add flags to a shared vm_map_range lock. */ extern void vm_map_range_sh_lock_add_flags( vm_map_lock_ctx_t vml_ctx, vmrl_sh_flags_t flags); /*! * @function vm_map_range_ex_lock_remove_flags * * @brief * Remove flags from a shared vm_map_range lock. */ extern void vm_map_range_sh_lock_remove_flags( vm_map_lock_ctx_t vml_ctx, vmrl_sh_flags_t flags); #pragma mark Simplification /*! * @function vm_map_locked_entry_simplify * * @brief * Attempts to coalesce the provided entry with its neighbors. * * @warning This is intended for use by functions engaging in manual locking * only. Clients using the range lock should use VMRL_SIMPLIFY. Clients * using the single entry lock (vm_map_find_entry_*) do not need to * simplify today. If the need arises, they should implement * VMRL_SIMPLIFY support for the single entry lock instead of * attempting to make this work with the single entry lock. * * @param [in] map The map in which simplification will take place. Its * interlock should not be held. * @param [in] entry The entry to be simplified. It should be locked. Its * neighboring entries should not have their locks held * by the caller. * * @return The entry resulting from the simplification. It may not be the same * as the one passed in (see rdar://150789194). Callers should stop * using the entry pointer they passed in, and work on this returned * entry instead. It will be locked. No other locks will be returned * held. */ __result_use_check extern vm_map_entry_t vm_map_locked_entry_simplify( vm_map_t map, vm_map_entry_t entry); __END_DECLS #endif /* _VM_VM_MAP_LOCK_H_ */ |