Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/damon: add damon_ctx->min_sz_region

Adopting addr_unit would make DAMON_MINREGION 'addr_unit * 4096' bytes and
cause data alignment issues[1].

Add damon_ctx->min_sz_region to change DAMON_MIN_REGION from a global
macro value to per-context variable.

Link: https://lkml.kernel.org/r/20250828171242.59810-12-sj@kernel.org
Link: https://lore.kernel.org/all/527714dd-0e33-43ab-bbbd-d89670ba79e7@huawei.com [1]
Signed-off-by: Quanmin Yan <yanquanmin1@huawei.com>
Signed-off-by: SeongJae Park <sj@kernel.org>
Reviewed-by: SeongJae Park <sj@kernel.org>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: ze zuo <zuoze1@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Quanmin Yan and committed by
Andrew Morton
d8f867fa 56cd1940

+61 -43
+3 -1
include/linux/damon.h
··· 747 747 * 748 748 * @ops: Set of monitoring operations for given use cases. 749 749 * @addr_unit: Scale factor for core to ops address conversion. 750 + * @min_sz_region: Minimum region size. 750 751 * @adaptive_targets: Head of monitoring targets (&damon_target) list. 751 752 * @schemes: Head of schemes (&damos) list. 752 753 */ ··· 790 789 791 790 struct damon_operations ops; 792 791 unsigned long addr_unit; 792 + unsigned long min_sz_region; 793 793 794 794 struct list_head adaptive_targets; 795 795 struct list_head schemes; ··· 879 877 void damon_add_region(struct damon_region *r, struct damon_target *t); 880 878 void damon_destroy_region(struct damon_region *r, struct damon_target *t); 881 879 int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, 882 - unsigned int nr_ranges); 880 + unsigned int nr_ranges, unsigned long min_sz_region); 883 881 void damon_update_region_access_rate(struct damon_region *r, bool accessed, 884 882 struct damon_attrs *attrs); 885 883
+38 -29
mm/damon/core.c
··· 201 201 * @t: the given target. 202 202 * @ranges: array of new monitoring target ranges. 203 203 * @nr_ranges: length of @ranges. 204 + * @min_sz_region: minimum region size. 204 205 * 205 206 * This function adds new regions to, or modify existing regions of a 206 207 * monitoring target to fit in specific ranges. ··· 209 208 * Return: 0 if success, or negative error code otherwise. 210 209 */ 211 210 int damon_set_regions(struct damon_target *t, struct damon_addr_range *ranges, 212 - unsigned int nr_ranges) 211 + unsigned int nr_ranges, unsigned long min_sz_region) 213 212 { 214 213 struct damon_region *r, *next; 215 214 unsigned int i; ··· 246 245 /* no region intersects with this range */ 247 246 newr = damon_new_region( 248 247 ALIGN_DOWN(range->start, 249 - DAMON_MIN_REGION), 250 - ALIGN(range->end, DAMON_MIN_REGION)); 248 + min_sz_region), 249 + ALIGN(range->end, min_sz_region)); 251 250 if (!newr) 252 251 return -ENOMEM; 253 252 damon_insert_region(newr, damon_prev_region(r), r, t); 254 253 } else { 255 254 /* resize intersecting regions to fit in this range */ 256 255 first->ar.start = ALIGN_DOWN(range->start, 257 - DAMON_MIN_REGION); 258 - last->ar.end = ALIGN(range->end, DAMON_MIN_REGION); 256 + min_sz_region); 257 + last->ar.end = ALIGN(range->end, min_sz_region); 259 258 260 259 /* fill possible holes in the range */ 261 260 err = damon_fill_regions_holes(first, last, t); ··· 546 545 ctx->attrs.max_nr_regions = 1000; 547 546 548 547 ctx->addr_unit = 1; 548 + ctx->min_sz_region = DAMON_MIN_REGION; 549 549 550 550 INIT_LIST_HEAD(&ctx->adaptive_targets); 551 551 INIT_LIST_HEAD(&ctx->schemes); ··· 1129 1127 * 1130 1128 * If @src has no region, @dst keeps current regions. 1131 1129 */ 1132 - static int damon_commit_target_regions( 1133 - struct damon_target *dst, struct damon_target *src) 1130 + static int damon_commit_target_regions(struct damon_target *dst, 1131 + struct damon_target *src, unsigned long src_min_sz_region) 1134 1132 { 1135 1133 struct damon_region *src_region; 1136 1134 struct damon_addr_range *ranges; ··· 1147 1145 i = 0; 1148 1146 damon_for_each_region(src_region, src) 1149 1147 ranges[i++] = src_region->ar; 1150 - err = damon_set_regions(dst, ranges, i); 1148 + err = damon_set_regions(dst, ranges, i, src_min_sz_region); 1151 1149 kfree(ranges); 1152 1150 return err; 1153 1151 } 1154 1152 1155 1153 static int damon_commit_target( 1156 1154 struct damon_target *dst, bool dst_has_pid, 1157 - struct damon_target *src, bool src_has_pid) 1155 + struct damon_target *src, bool src_has_pid, 1156 + unsigned long src_min_sz_region) 1158 1157 { 1159 1158 int err; 1160 1159 1161 - err = damon_commit_target_regions(dst, src); 1160 + err = damon_commit_target_regions(dst, src, src_min_sz_region); 1162 1161 if (err) 1163 1162 return err; 1164 1163 if (dst_has_pid) ··· 1181 1178 if (src_target) { 1182 1179 err = damon_commit_target( 1183 1180 dst_target, damon_target_has_pid(dst), 1184 - src_target, damon_target_has_pid(src)); 1181 + src_target, damon_target_has_pid(src), 1182 + src->min_sz_region); 1185 1183 if (err) 1186 1184 return err; 1187 1185 } else { ··· 1205 1201 if (!new_target) 1206 1202 return -ENOMEM; 1207 1203 err = damon_commit_target(new_target, false, 1208 - src_target, damon_target_has_pid(src)); 1204 + src_target, damon_target_has_pid(src), 1205 + src->min_sz_region); 1209 1206 if (err) { 1210 1207 damon_destroy_target(new_target, NULL); 1211 1208 return err; ··· 1253 1248 } 1254 1249 dst->ops = src->ops; 1255 1250 dst->addr_unit = src->addr_unit; 1251 + dst->min_sz_region = src->min_sz_region; 1256 1252 1257 1253 return 0; 1258 1254 } ··· 1286 1280 1287 1281 if (ctx->attrs.min_nr_regions) 1288 1282 sz /= ctx->attrs.min_nr_regions; 1289 - if (sz < DAMON_MIN_REGION) 1290 - sz = DAMON_MIN_REGION; 1283 + if (sz < ctx->min_sz_region) 1284 + sz = ctx->min_sz_region; 1291 1285 1292 1286 return sz; 1293 1287 } ··· 1631 1625 * @t: The target of the region. 1632 1626 * @rp: The pointer to the region. 1633 1627 * @s: The scheme to be applied. 1628 + * @min_sz_region: minimum region size. 1634 1629 * 1635 1630 * If a quota of a scheme has exceeded in a quota charge window, the scheme's 1636 1631 * action would applied to only a part of the target access pattern fulfilling ··· 1649 1642 * Return: true if the region should be entirely skipped, false otherwise. 1650 1643 */ 1651 1644 static bool damos_skip_charged_region(struct damon_target *t, 1652 - struct damon_region **rp, struct damos *s) 1645 + struct damon_region **rp, struct damos *s, unsigned long min_sz_region) 1653 1646 { 1654 1647 struct damon_region *r = *rp; 1655 1648 struct damos_quota *quota = &s->quota; ··· 1671 1664 if (quota->charge_addr_from && r->ar.start < 1672 1665 quota->charge_addr_from) { 1673 1666 sz_to_skip = ALIGN_DOWN(quota->charge_addr_from - 1674 - r->ar.start, DAMON_MIN_REGION); 1667 + r->ar.start, min_sz_region); 1675 1668 if (!sz_to_skip) { 1676 - if (damon_sz_region(r) <= DAMON_MIN_REGION) 1669 + if (damon_sz_region(r) <= min_sz_region) 1677 1670 return true; 1678 - sz_to_skip = DAMON_MIN_REGION; 1671 + sz_to_skip = min_sz_region; 1679 1672 } 1680 1673 damon_split_region_at(t, r, sz_to_skip); 1681 1674 r = damon_next_region(r); ··· 1700 1693 } 1701 1694 1702 1695 static bool damos_filter_match(struct damon_ctx *ctx, struct damon_target *t, 1703 - struct damon_region *r, struct damos_filter *filter) 1696 + struct damon_region *r, struct damos_filter *filter, 1697 + unsigned long min_sz_region) 1704 1698 { 1705 1699 bool matched = false; 1706 1700 struct damon_target *ti; ··· 1718 1710 matched = target_idx == filter->target_idx; 1719 1711 break; 1720 1712 case DAMOS_FILTER_TYPE_ADDR: 1721 - start = ALIGN_DOWN(filter->addr_range.start, DAMON_MIN_REGION); 1722 - end = ALIGN_DOWN(filter->addr_range.end, DAMON_MIN_REGION); 1713 + start = ALIGN_DOWN(filter->addr_range.start, min_sz_region); 1714 + end = ALIGN_DOWN(filter->addr_range.end, min_sz_region); 1723 1715 1724 1716 /* inside the range */ 1725 1717 if (start <= r->ar.start && r->ar.end <= end) { ··· 1755 1747 1756 1748 s->core_filters_allowed = false; 1757 1749 damos_for_each_filter(filter, s) { 1758 - if (damos_filter_match(ctx, t, r, filter)) { 1750 + if (damos_filter_match(ctx, t, r, filter, ctx->min_sz_region)) { 1759 1751 if (filter->allow) 1760 1752 s->core_filters_allowed = true; 1761 1753 return !filter->allow; ··· 1890 1882 if (c->ops.apply_scheme) { 1891 1883 if (quota->esz && quota->charged_sz + sz > quota->esz) { 1892 1884 sz = ALIGN_DOWN(quota->esz - quota->charged_sz, 1893 - DAMON_MIN_REGION); 1885 + c->min_sz_region); 1894 1886 if (!sz) 1895 1887 goto update_stat; 1896 1888 damon_split_region_at(t, r, sz); ··· 1938 1930 if (quota->esz && quota->charged_sz >= quota->esz) 1939 1931 continue; 1940 1932 1941 - if (damos_skip_charged_region(t, &r, s)) 1933 + if (damos_skip_charged_region(t, &r, s, c->min_sz_region)) 1942 1934 continue; 1943 1935 1944 1936 if (!damos_valid_target(c, t, r, s)) ··· 2332 2324 } 2333 2325 2334 2326 /* Split every region in the given target into 'nr_subs' regions */ 2335 - static void damon_split_regions_of(struct damon_target *t, int nr_subs) 2327 + static void damon_split_regions_of(struct damon_target *t, int nr_subs, 2328 + unsigned long min_sz_region) 2336 2329 { 2337 2330 struct damon_region *r, *next; 2338 2331 unsigned long sz_region, sz_sub = 0; ··· 2343 2334 sz_region = damon_sz_region(r); 2344 2335 2345 2336 for (i = 0; i < nr_subs - 1 && 2346 - sz_region > 2 * DAMON_MIN_REGION; i++) { 2337 + sz_region > 2 * min_sz_region; i++) { 2347 2338 /* 2348 2339 * Randomly select size of left sub-region to be at 2349 2340 * least 10 percent and at most 90% of original region 2350 2341 */ 2351 2342 sz_sub = ALIGN_DOWN(damon_rand(1, 10) * 2352 - sz_region / 10, DAMON_MIN_REGION); 2343 + sz_region / 10, min_sz_region); 2353 2344 /* Do not allow blank region */ 2354 2345 if (sz_sub == 0 || sz_sub >= sz_region) 2355 2346 continue; ··· 2389 2380 nr_subregions = 3; 2390 2381 2391 2382 damon_for_each_target(t, ctx) 2392 - damon_split_regions_of(t, nr_subregions); 2383 + damon_split_regions_of(t, nr_subregions, ctx->min_sz_region); 2393 2384 2394 2385 last_nr_regions = nr_regions; 2395 2386 } ··· 2778 2769 2779 2770 addr_range.start = *start; 2780 2771 addr_range.end = *end; 2781 - return damon_set_regions(t, &addr_range, 1); 2772 + return damon_set_regions(t, &addr_range, 1, DAMON_MIN_REGION); 2782 2773 } 2783 2774 2784 2775 /*
+5 -3
mm/damon/sysfs.c
··· 1329 1329 } 1330 1330 1331 1331 static int damon_sysfs_set_regions(struct damon_target *t, 1332 - struct damon_sysfs_regions *sysfs_regions) 1332 + struct damon_sysfs_regions *sysfs_regions, 1333 + unsigned long min_sz_region) 1333 1334 { 1334 1335 struct damon_addr_range *ranges = kmalloc_array(sysfs_regions->nr, 1335 1336 sizeof(*ranges), GFP_KERNEL | __GFP_NOWARN); ··· 1352 1351 if (ranges[i - 1].end > ranges[i].start) 1353 1352 goto out; 1354 1353 } 1355 - err = damon_set_regions(t, ranges, sysfs_regions->nr); 1354 + err = damon_set_regions(t, ranges, sysfs_regions->nr, min_sz_region); 1356 1355 out: 1357 1356 kfree(ranges); 1358 1357 return err; ··· 1373 1372 /* caller will destroy targets */ 1374 1373 return -EINVAL; 1375 1374 } 1376 - return damon_sysfs_set_regions(t, sys_target->regions); 1375 + return damon_sysfs_set_regions(t, sys_target->regions, ctx->min_sz_region); 1377 1376 } 1378 1377 1379 1378 static int damon_sysfs_add_targets(struct damon_ctx *ctx, ··· 1431 1430 if (err) 1432 1431 return err; 1433 1432 ctx->addr_unit = sys_ctx->addr_unit; 1433 + ctx->min_sz_region = max(DAMON_MIN_REGION / sys_ctx->addr_unit, 1); 1434 1434 err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs); 1435 1435 if (err) 1436 1436 return err;
+13 -8
mm/damon/tests/core-kunit.h
··· 230 230 t = damon_new_target(); 231 231 r = damon_new_region(0, 22); 232 232 damon_add_region(r, t); 233 - damon_split_regions_of(t, 2); 233 + damon_split_regions_of(t, 2, DAMON_MIN_REGION); 234 234 KUNIT_EXPECT_LE(test, damon_nr_regions(t), 2u); 235 235 damon_free_target(t); 236 236 237 237 t = damon_new_target(); 238 238 r = damon_new_region(0, 220); 239 239 damon_add_region(r, t); 240 - damon_split_regions_of(t, 4); 240 + damon_split_regions_of(t, 4, DAMON_MIN_REGION); 241 241 KUNIT_EXPECT_LE(test, damon_nr_regions(t), 4u); 242 242 damon_free_target(t); 243 243 damon_destroy_ctx(c); ··· 303 303 304 304 damon_add_region(r1, t); 305 305 damon_add_region(r2, t); 306 - damon_set_regions(t, &range, 1); 306 + damon_set_regions(t, &range, 1, DAMON_MIN_REGION); 307 307 308 308 KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 3); 309 309 damon_for_each_region(r, t) { ··· 450 450 damon_add_region(r, t); 451 451 452 452 /* region in the range */ 453 - KUNIT_EXPECT_TRUE(test, damos_filter_match(NULL, t, r, f)); 453 + KUNIT_EXPECT_TRUE(test, 454 + damos_filter_match(NULL, t, r, f, DAMON_MIN_REGION)); 454 455 KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1); 455 456 456 457 /* region before the range */ 457 458 r->ar.start = DAMON_MIN_REGION * 1; 458 459 r->ar.end = DAMON_MIN_REGION * 2; 459 - KUNIT_EXPECT_FALSE(test, damos_filter_match(NULL, t, r, f)); 460 + KUNIT_EXPECT_FALSE(test, 461 + damos_filter_match(NULL, t, r, f, DAMON_MIN_REGION)); 460 462 KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1); 461 463 462 464 /* region after the range */ 463 465 r->ar.start = DAMON_MIN_REGION * 6; 464 466 r->ar.end = DAMON_MIN_REGION * 8; 465 - KUNIT_EXPECT_FALSE(test, damos_filter_match(NULL, t, r, f)); 467 + KUNIT_EXPECT_FALSE(test, 468 + damos_filter_match(NULL, t, r, f, DAMON_MIN_REGION)); 466 469 KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1); 467 470 468 471 /* region started before the range */ 469 472 r->ar.start = DAMON_MIN_REGION * 1; 470 473 r->ar.end = DAMON_MIN_REGION * 4; 471 - KUNIT_EXPECT_FALSE(test, damos_filter_match(NULL, t, r, f)); 474 + KUNIT_EXPECT_FALSE(test, 475 + damos_filter_match(NULL, t, r, f, DAMON_MIN_REGION)); 472 476 /* filter should have split the region */ 473 477 KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 1); 474 478 KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 2); ··· 485 481 /* region started in the range */ 486 482 r->ar.start = DAMON_MIN_REGION * 2; 487 483 r->ar.end = DAMON_MIN_REGION * 8; 488 - KUNIT_EXPECT_TRUE(test, damos_filter_match(NULL, t, r, f)); 484 + KUNIT_EXPECT_TRUE(test, 485 + damos_filter_match(NULL, t, r, f, DAMON_MIN_REGION)); 489 486 /* filter should have split the region */ 490 487 KUNIT_EXPECT_EQ(test, r->ar.start, DAMON_MIN_REGION * 2); 491 488 KUNIT_EXPECT_EQ(test, r->ar.end, DAMON_MIN_REGION * 6);
+1 -1
mm/damon/tests/vaddr-kunit.h
··· 141 141 damon_add_region(r, t); 142 142 } 143 143 144 - damon_set_regions(t, three_regions, 3); 144 + damon_set_regions(t, three_regions, 3, DAMON_MIN_REGION); 145 145 146 146 for (i = 0; i < nr_expected / 2; i++) { 147 147 r = __nth_region_of(t, i);
+1 -1
mm/damon/vaddr.c
··· 299 299 damon_for_each_target(t, ctx) { 300 300 if (damon_va_three_regions(t, three_regions)) 301 301 continue; 302 - damon_set_regions(t, three_regions, 3); 302 + damon_set_regions(t, three_regions, 3, DAMON_MIN_REGION); 303 303 } 304 304 } 305 305