| @@ -19,6 +19,13 @@ · 18 unmodified lines |
| 19 | 19 | * for anonymous memory. For process policy an process counter |
| 20 | 20 | * is used. |
| 21 | 21 | * |
| 22 | + * weighted interleave |
| 23 | + * Allocate memory interleaved over a set of nodes based on |
| 24 | + * a set of weights (per-node), with normal fallback if it |
| 25 | + * fails. Otherwise operates the same as interleave. |
| 26 | + * Example: nodeset(0,1) & weights (2,1) - 2 pages allocated |
| 27 | + * on node 0 for every 1 page allocated on node 1. |
| 28 | + * |
| 22 | 29 | * bind Only allocate memory on a specific set of nodes, |
| 23 | 30 | * no fallback. |
| 24 | 31 | * FIXME: memory is allocated starting with the first node |
| @@ -441,6 +448,10 @@ static const struct mempolicy_operations mpol_ops[MPOL_MAX] = { · 416 unmodified lines |
| 441 | 448 | .create = mpol_new_nodemask, |
| 442 | 449 | .rebind = mpol_rebind_preferred, |
| 443 | 450 | }, |
| 451 | + [MPOL_WEIGHTED_INTERLEAVE] = { |
| 452 | + .create = mpol_new_nodemask, |
| 453 | + .rebind = mpol_rebind_nodemask, |
| 454 | + }, |
| 444 | 455 | }; |
| 445 | 456 | |
| 446 | 457 | static bool migrate_folio_add(struct folio *folio, struct list_head *foliolist, |
| @@ -858,8 +869,11 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags, · 411 unmodified lines |
| 858 | 869 | |
| 859 | 870 | old = current->mempolicy; |
| 860 | 871 | current->mempolicy = new; |
| 861 | | − if (new && new->mode == MPOL_INTERLEAVE) |
| 872 | + if (new && (new->mode == MPOL_INTERLEAVE || |
| 873 | + new->mode == MPOL_WEIGHTED_INTERLEAVE)) { |
| 862 | 874 | current->il_prev = MAX_NUMNODES-1; |
| 875 | + current->il_weight = 0; |
| 876 | + } |
| 863 | 877 | task_unlock(current); |
| 864 | 878 | mpol_put(old); |
| 865 | 879 | ret = 0; |
| @@ -884,6 +898,7 @@ static void get_policy_nodemask(struct mempolicy *pol, nodemask_t *nodes) · 18 unmodified lines |
| 884 | 898 | case MPOL_INTERLEAVE: |
| 885 | 899 | case MPOL_PREFERRED: |
| 886 | 900 | case MPOL_PREFERRED_MANY: |
| 901 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 887 | 902 | *nodes = pol->nodes; |
| 888 | 903 | break; |
| 889 | 904 | case MPOL_LOCAL: |
| @@ -968,6 +983,13 @@ static long do_get_mempolicy(int *policy, nodemask_t *nmask, · 78 unmodified lines |
| 968 | 983 | } else if (pol == current->mempolicy && |
| 969 | 984 | pol->mode == MPOL_INTERLEAVE) { |
| 970 | 985 | *policy = next_node_in(current->il_prev, pol->nodes); |
| 986 | + } else if (pol == current->mempolicy && |
| 987 | + pol->mode == MPOL_WEIGHTED_INTERLEAVE) { |
| 988 | + if (current->il_weight) |
| 989 | + *policy = current->il_prev; |
| 990 | + else |
| 991 | + *policy = next_node_in(current->il_prev, |
| 992 | + pol->nodes); |
| 971 | 993 | } else { |
| 972 | 994 | err = -EINVAL; |
| 973 | 995 | goto out; |
| @@ -1332,7 +1354,8 @@ static long do_mbind(unsigned long start, unsigned long len, · 358 unmodified lines |
| 1332 | 1354 | * VMAs, the nodes will still be interleaved from the targeted |
| 1333 | 1355 | * nodemask, but one by one may be selected differently. |
| 1334 | 1356 | */ |
| 1335 | | − if (new->mode == MPOL_INTERLEAVE) { |
| 1357 | + if (new->mode == MPOL_INTERLEAVE || |
| 1358 | + new->mode == MPOL_WEIGHTED_INTERLEAVE) { |
| 1336 | 1359 | struct page *page; |
| 1337 | 1360 | unsigned int order; |
| 1338 | 1361 | unsigned long addr = -EFAULT; |
| @@ -1780,7 +1803,8 @@ struct mempolicy *__get_vma_policy(struct vm_area_struct *vma, · 441 unmodified lines |
| 1780 | 1803 | * @vma: virtual memory area whose policy is sought |
| 1781 | 1804 | * @addr: address in @vma for shared policy lookup |
| 1782 | 1805 | * @order: 0, or appropriate huge_page_order for interleaving |
| 1783 | | − * @ilx: interleave index (output), for use only when MPOL_INTERLEAVE |
| 1806 | + * @ilx: interleave index (output), for use only when MPOL_INTERLEAVE or |
| 1807 | + * MPOL_WEIGHTED_INTERLEAVE |
| 1784 | 1808 | * |
| 1785 | 1809 | * Returns effective policy for a VMA at specified address. |
| 1786 | 1810 | * Falls back to current->mempolicy or system default policy, as necessary. |
| @@ -1797,7 +1821,8 @@ struct mempolicy *get_vma_policy(struct vm_area_struct *vma, · 10 unmodified lines |
| 1797 | 1821 | pol = __get_vma_policy(vma, addr, ilx); |
| 1798 | 1822 | if (!pol) |
| 1799 | 1823 | pol = get_task_policy(current); |
| 1800 | | − if (pol->mode == MPOL_INTERLEAVE) { |
| 1824 | + if (pol->mode == MPOL_INTERLEAVE || |
| 1825 | + pol->mode == MPOL_WEIGHTED_INTERLEAVE) { |
| 1801 | 1826 | *ilx += vma->vm_pgoff >> order; |
| 1802 | 1827 | *ilx += (addr - vma->vm_start) >> (PAGE_SHIFT + order); |
| 1803 | 1828 | } |
| @@ -1847,6 +1872,22 @@ bool apply_policy_zone(struct mempolicy *policy, enum zone_type zone) · 43 unmodified lines |
| 1847 | 1872 | return zone >= dynamic_policy_zone; |
| 1848 | 1873 | } |
| 1849 | 1874 | |
| 1875 | +static unsigned int weighted_interleave_nodes(struct mempolicy *policy) |
| 1876 | +{ |
| 1877 | + unsigned int node = current->il_prev; |
| 1878 | + |
| 1879 | + if (!current->il_weight || !node_isset(node, policy->nodes)) { |
| 1880 | + node = next_node_in(node, policy->nodes); |
| 1881 | + /* can only happen if nodemask is being rebound */ |
| 1882 | + if (node == MAX_NUMNODES) |
| 1883 | + return node; |
| 1884 | + current->il_prev = node; |
| 1885 | + current->il_weight = get_il_weight(node); |
| 1886 | + } |
| 1887 | + current->il_weight--; |
| 1888 | + return node; |
| 1889 | +} |
| 1890 | + |
| 1850 | 1891 | /* Do dynamic interleaving for a process */ |
| 1851 | 1892 | static unsigned int interleave_nodes(struct mempolicy *policy) |
| 1852 | 1893 | { |
| @@ -1881,6 +1922,9 @@ unsigned int mempolicy_slab_node(void) · 28 unmodified lines |
| 1881 | 1922 | case MPOL_INTERLEAVE: |
| 1882 | 1923 | return interleave_nodes(policy); |
| 1883 | 1924 | |
| 1925 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 1926 | + return weighted_interleave_nodes(policy); |
| 1927 | + |
| 1884 | 1928 | case MPOL_BIND: |
| 1885 | 1929 | case MPOL_PREFERRED_MANY: |
| 1886 | 1930 | { |
| @@ -1919,6 +1963,45 @@ static unsigned int read_once_policy_nodemask(struct mempolicy *pol, · 32 unmodified lines |
| 1919 | 1963 | return nodes_weight(*mask); |
| 1920 | 1964 | } |
| 1921 | 1965 | |
| 1966 | +static unsigned int weighted_interleave_nid(struct mempolicy *pol, pgoff_t ilx) |
| 1967 | +{ |
| 1968 | + nodemask_t nodemask; |
| 1969 | + unsigned int target, nr_nodes; |
| 1970 | + u8 *table; |
| 1971 | + unsigned int weight_total = 0; |
| 1972 | + u8 weight; |
| 1973 | + int nid; |
| 1974 | + |
| 1975 | + nr_nodes = read_once_policy_nodemask(pol, &nodemask); |
| 1976 | + if (!nr_nodes) |
| 1977 | + return numa_node_id(); |
| 1978 | + |
| 1979 | + rcu_read_lock(); |
| 1980 | + table = rcu_dereference(iw_table); |
| 1981 | + /* calculate the total weight */ |
| 1982 | + for_each_node_mask(nid, nodemask) { |
| 1983 | + /* detect system default usage */ |
| 1984 | + weight = table ? table[nid] : 1; |
| 1985 | + weight = weight ? weight : 1; |
| 1986 | + weight_total += weight; |
| 1987 | + } |
| 1988 | + |
| 1989 | + /* Calculate the node offset based on totals */ |
| 1990 | + target = ilx % weight_total; |
| 1991 | + nid = first_node(nodemask); |
| 1992 | + while (target) { |
| 1993 | + /* detect system default usage */ |
| 1994 | + weight = table ? table[nid] : 1; |
| 1995 | + weight = weight ? weight : 1; |
| 1996 | + if (target < weight) |
| 1997 | + break; |
| 1998 | + target -= weight; |
| 1999 | + nid = next_node_in(nid, nodemask); |
| 2000 | + } |
| 2001 | + rcu_read_unlock(); |
| 2002 | + return nid; |
| 2003 | +} |
| 2004 | + |
| 1922 | 2005 | /* |
| 1923 | 2006 | * Do static interleaving for interleave index @ilx. Returns the ilx'th |
| 1924 | 2007 | * node in pol->nodes (starting from ilx=0), wrapping around if ilx |
| @@ -1979,6 +2062,11 @@ static nodemask_t *policy_nodemask(gfp_t gfp, struct mempolicy *pol, · 54 unmodified lines |
| 1979 | 2062 | *nid = (ilx == NO_INTERLEAVE_INDEX) ? |
| 1980 | 2063 | interleave_nodes(pol) : interleave_nid(pol, ilx); |
| 1981 | 2064 | break; |
| 2065 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 2066 | + *nid = (ilx == NO_INTERLEAVE_INDEX) ? |
| 2067 | + weighted_interleave_nodes(pol) : |
| 2068 | + weighted_interleave_nid(pol, ilx); |
| 2069 | + break; |
| 1982 | 2070 | } |
| 1983 | 2071 | |
| 1984 | 2072 | return nodemask; |
| @@ -2040,6 +2128,7 @@ bool init_nodemask_of_mempolicy(nodemask_t *mask) · 55 unmodified lines |
| 2040 | 2128 | case MPOL_PREFERRED_MANY: |
| 2041 | 2129 | case MPOL_BIND: |
| 2042 | 2130 | case MPOL_INTERLEAVE: |
| 2131 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 2043 | 2132 | *mask = mempolicy->nodes; |
| 2044 | 2133 | break; |
| 2045 | 2134 | |
| @@ -2140,6 +2229,7 @@ struct page *alloc_pages_mpol(gfp_t gfp, unsigned int order, · 94 unmodified lines |
| 2140 | 2229 | * node in its nodemask, we allocate the standard way. |
| 2141 | 2230 | */ |
| 2142 | 2231 | if (pol->mode != MPOL_INTERLEAVE && |
| 2232 | + pol->mode != MPOL_WEIGHTED_INTERLEAVE && |
| 2143 | 2233 | (!nodemask || node_isset(nid, *nodemask))) { |
| 2144 | 2234 | /* |
| 2145 | 2235 | * First, try to allocate THP only on local node, but |
| @@ -2275,6 +2365,114 @@ static unsigned long alloc_pages_bulk_array_interleave(gfp_t gfp, · 129 unmodified lines |
| 2275 | 2365 | return total_allocated; |
| 2276 | 2366 | } |
| 2277 | 2367 | |
| 2368 | +static unsigned long alloc_pages_bulk_array_weighted_interleave(gfp_t gfp, |
| 2369 | + struct mempolicy *pol, unsigned long nr_pages, |
| 2370 | + struct page **page_array) |
| 2371 | +{ |
| 2372 | + struct task_struct *me = current; |
| 2373 | + unsigned long total_allocated = 0; |
| 2374 | + unsigned long nr_allocated = 0; |
| 2375 | + unsigned long rounds; |
| 2376 | + unsigned long node_pages, delta; |
| 2377 | + u8 *table, *weights, weight; |
| 2378 | + unsigned int weight_total = 0; |
| 2379 | + unsigned long rem_pages = nr_pages; |
| 2380 | + nodemask_t nodes; |
| 2381 | + int nnodes, node; |
| 2382 | + int resume_node = MAX_NUMNODES - 1; |
| 2383 | + u8 resume_weight = 0; |
| 2384 | + int prev_node; |
| 2385 | + int i; |
| 2386 | + |
| 2387 | + if (!nr_pages) |
| 2388 | + return 0; |
| 2389 | + |
| 2390 | + nnodes = read_once_policy_nodemask(pol, &nodes); |
| 2391 | + if (!nnodes) |
| 2392 | + return 0; |
| 2393 | + |
| 2394 | + /* Continue allocating from most recent node and adjust the nr_pages */ |
| 2395 | + node = me->il_prev; |
| 2396 | + weight = me->il_weight; |
| 2397 | + if (weight && node_isset(node, nodes)) { |
| 2398 | + node_pages = min(rem_pages, weight); |
| 2399 | + nr_allocated = __alloc_pages_bulk(gfp, node, NULL, node_pages, |
| 2400 | + NULL, page_array); |
| 2401 | + page_array += nr_allocated; |
| 2402 | + total_allocated += nr_allocated; |
| 2403 | + /* if that's all the pages, no need to interleave */ |
| 2404 | + if (rem_pages <= weight) { |
| 2405 | + me->il_weight -= rem_pages; |
| 2406 | + return total_allocated; |
| 2407 | + } |
| 2408 | + /* Otherwise we adjust remaining pages, continue from there */ |
| 2409 | + rem_pages -= weight; |
| 2410 | + } |
| 2411 | + /* clear active weight in case of an allocation failure */ |
| 2412 | + me->il_weight = 0; |
| 2413 | + prev_node = node; |
| 2414 | + |
| 2415 | + /* create a local copy of node weights to operate on outside rcu */ |
| 2416 | + weights = kzalloc(nr_node_ids, GFP_KERNEL); |
| 2417 | + if (!weights) |
| 2418 | + return total_allocated; |
| 2419 | + |
| 2420 | + rcu_read_lock(); |
| 2421 | + table = rcu_dereference(iw_table); |
| 2422 | + if (table) |
| 2423 | + memcpy(weights, table, nr_node_ids); |
| 2424 | + rcu_read_unlock(); |
| 2425 | + |
| 2426 | + /* calculate total, detect system default usage */ |
| 2427 | + for_each_node_mask(node, nodes) { |
| 2428 | + if (!weights[node]) |
| 2429 | + weights[node] = 1; |
| 2430 | + weight_total += weights[node]; |
| 2431 | + } |
| 2432 | + |
| 2433 | + /* |
| 2434 | + * Calculate rounds/partial rounds to minimize __alloc_pages_bulk calls. |
| 2435 | + * Track which node weighted interleave should resume from. |
| 2436 | + * |
| 2437 | + * if (rounds > 0) and (delta == 0), resume_node will always be |
| 2438 | + * the node following prev_node and its weight. |
| 2439 | + */ |
| 2440 | + rounds = rem_pages / weight_total; |
| 2441 | + delta = rem_pages % weight_total; |
| 2442 | + resume_node = next_node_in(prev_node, nodes); |
| 2443 | + resume_weight = weights[resume_node]; |
| 2444 | + for (i = 0; i < nnodes; i++) { |
| 2445 | + node = next_node_in(prev_node, nodes); |
| 2446 | + weight = weights[node]; |
| 2447 | + node_pages = weight * rounds; |
| 2448 | + /* If a delta exists, add this node's portion of the delta */ |
| 2449 | + if (delta > weight) { |
| 2450 | + node_pages += weight; |
| 2451 | + delta -= weight; |
| 2452 | + } else if (delta) { |
| 2453 | + /* when delta is depleted, resume from that node */ |
| 2454 | + node_pages += delta; |
| 2455 | + resume_node = node; |
| 2456 | + resume_weight = weight - delta; |
| 2457 | + delta = 0; |
| 2458 | + } |
| 2459 | + /* node_pages can be 0 if an allocation fails and rounds == 0 */ |
| 2460 | + if (!node_pages) |
| 2461 | + break; |
| 2462 | + nr_allocated = __alloc_pages_bulk(gfp, node, NULL, node_pages, |
| 2463 | + NULL, page_array); |
| 2464 | + page_array += nr_allocated; |
| 2465 | + total_allocated += nr_allocated; |
| 2466 | + if (total_allocated == nr_pages) |
| 2467 | + break; |
| 2468 | + prev_node = node; |
| 2469 | + } |
| 2470 | + me->il_prev = resume_node; |
| 2471 | + me->il_weight = resume_weight; |
| 2472 | + kfree(weights); |
| 2473 | + return total_allocated; |
| 2474 | +} |
| 2475 | + |
| 2278 | 2476 | static unsigned long alloc_pages_bulk_array_preferred_many(gfp_t gfp, int nid, |
| 2279 | 2477 | struct mempolicy *pol, unsigned long nr_pages, |
| 2280 | 2478 | struct page **page_array) |
| @@ -2315,6 +2513,10 @@ unsigned long alloc_pages_bulk_array_mempolicy(gfp_t gfp, · 34 unmodified lines |
| 2315 | 2513 | return alloc_pages_bulk_array_interleave(gfp, pol, |
| 2316 | 2514 | nr_pages, page_array); |
| 2317 | 2515 | |
| 2516 | + if (pol->mode == MPOL_WEIGHTED_INTERLEAVE) |
| 2517 | + return alloc_pages_bulk_array_weighted_interleave( |
| 2518 | + gfp, pol, nr_pages, page_array); |
| 2519 | + |
| 2318 | 2520 | if (pol->mode == MPOL_PREFERRED_MANY) |
| 2319 | 2521 | return alloc_pages_bulk_array_preferred_many(gfp, |
| 2320 | 2522 | numa_node_id(), pol, nr_pages, page_array); |
| @@ -2390,6 +2592,7 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b) · 69 unmodified lines |
| 2390 | 2592 | case MPOL_INTERLEAVE: |
| 2391 | 2593 | case MPOL_PREFERRED: |
| 2392 | 2594 | case MPOL_PREFERRED_MANY: |
| 2595 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 2393 | 2596 | return !!nodes_equal(a->nodes, b->nodes); |
| 2394 | 2597 | case MPOL_LOCAL: |
| 2395 | 2598 | return true; |
| @@ -2526,6 +2729,10 @@ int mpol_misplaced(struct folio *folio, struct vm_area_struct *vma, · 130 unmodified lines |
| 2526 | 2729 | polnid = interleave_nid(pol, ilx); |
| 2527 | 2730 | break; |
| 2528 | 2731 | |
| 2732 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 2733 | + polnid = weighted_interleave_nid(pol, ilx); |
| 2734 | + break; |
| 2735 | + |
| 2529 | 2736 | case MPOL_PREFERRED: |
| 2530 | 2737 | if (node_isset(curnid, pol->nodes)) |
| 2531 | 2738 | goto out; |
| @@ -2900,6 +3107,7 @@ static const char * const policy_modes[] = · 368 unmodified lines |
| 2900 | 3107 | [MPOL_PREFERRED] = "prefer", |
| 2901 | 3108 | [MPOL_BIND] = "bind", |
| 2902 | 3109 | [MPOL_INTERLEAVE] = "interleave", |
| 3110 | + [MPOL_WEIGHTED_INTERLEAVE] = "weighted interleave", |
| 2903 | 3111 | [MPOL_LOCAL] = "local", |
| 2904 | 3112 | [MPOL_PREFERRED_MANY] = "prefer (many)", |
| 2905 | 3113 | }; |
| @@ -2959,6 +3167,7 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) · 53 unmodified lines |
| 2959 | 3167 | } |
| 2960 | 3168 | break; |
| 2961 | 3169 | case MPOL_INTERLEAVE: |
| 3170 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 2962 | 3171 | /* |
| 2963 | 3172 | * Default to online nodes with memory if no nodelist |
| 2964 | 3173 | */ |
| @@ -3069,6 +3278,7 @@ void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol) · 104 unmodified lines |
| 3069 | 3278 | case MPOL_PREFERRED_MANY: |
| 3070 | 3279 | case MPOL_BIND: |
| 3071 | 3280 | case MPOL_INTERLEAVE: |
| 3281 | + case MPOL_WEIGHTED_INTERLEAVE: |
| 3072 | 3282 | nodes = pol->nodes; |
| 3073 | 3283 | break; |
| 3074 | 3284 | default: |