/* * 't->pid' should be the pointer to the relevant 'struct pid' having reference * count. Caller must put the returned task, unless it is NULL.
*/ staticinlinestruct task_struct *damon_get_task_struct(struct damon_target *t)
{ return get_pid_task(t->pid, PIDTYPE_PID);
}
/* * Get the mm_struct of the given target * * Caller _must_ put the mm_struct after use, unless it is NULL. * * Returns the mm_struct of the target on success, NULL on failure
*/ staticstruct mm_struct *damon_get_mm(struct damon_target *t)
{ struct task_struct *task; struct mm_struct *mm;
task = damon_get_task_struct(t); if (!task) return NULL;
mm = get_task_mm(task);
put_task_struct(task); return mm;
}
/* * Functions for the initial monitoring target regions construction
*/
/* * Size-evenly split a region into 'nr_pieces' small regions * * Returns 0 on success, or negative error code otherwise.
*/ staticint damon_va_evenly_split_region(struct damon_target *t, struct damon_region *r, unsignedint nr_pieces)
{ unsignedlong sz_orig, sz_piece, orig_end; struct damon_region *n = NULL, *next; unsignedlong start; unsignedint i;
r->ar.end = r->ar.start + sz_piece;
next = damon_next_region(r); for (start = r->ar.end, i = 1; i < nr_pieces; start += sz_piece, i++) {
n = damon_new_region(start, start + sz_piece); if (!n) return -ENOMEM;
damon_insert_region(n, r, next, t);
r = n;
} /* complement last region for possible rounding error */ if (n)
n->ar.end = orig_end;
/* * Find three regions separated by two biggest unmapped regions * * vma the head vma of the target address space * regions an array of three address ranges that results will be saved * * This function receives an address space and finds three regions in it which * separated by the two biggest unmapped regions in the space. Please refer to * below comments of '__damon_va_init_regions()' function to know why this is * necessary. * * Returns 0 if success, or negative error code otherwise.
*/ staticint __damon_va_three_regions(struct mm_struct *mm, struct damon_addr_range regions[3])
{ struct damon_addr_range first_gap = {0}, second_gap = {0};
VMA_ITERATOR(vmi, mm, 0); struct vm_area_struct *vma, *prev = NULL; unsignedlong start;
/* * Find the two biggest gaps so that first_gap > second_gap > others. * If this is too slow, it can be optimised to examine the maple * tree gaps.
*/
rcu_read_lock();
for_each_vma(vmi, vma) { unsignedlong gap;
if (!prev) {
start = vma->vm_start; goto next;
}
gap = vma->vm_start - prev->vm_end;
if (!sz_range(&second_gap) || !sz_range(&first_gap)) return -EINVAL;
/* Sort the two biggest gaps by address */ if (first_gap.start > second_gap.start)
swap(first_gap, second_gap);
/* Store the result */
regions[0].start = ALIGN(start, DAMON_MIN_REGION);
regions[0].end = ALIGN(first_gap.start, DAMON_MIN_REGION);
regions[1].start = ALIGN(first_gap.end, DAMON_MIN_REGION);
regions[1].end = ALIGN(second_gap.start, DAMON_MIN_REGION);
regions[2].start = ALIGN(second_gap.end, DAMON_MIN_REGION);
regions[2].end = ALIGN(prev->vm_end, DAMON_MIN_REGION);
return 0;
}
/* * Get the three regions in the given target (task) * * Returns 0 on success, negative error code otherwise.
*/ staticint damon_va_three_regions(struct damon_target *t, struct damon_addr_range regions[3])
{ struct mm_struct *mm; int rc;
/* * Initialize the monitoring target regions for the given target (task) * * t the given target * * Because only a number of small portions of the entire address space * is actually mapped to the memory and accessed, monitoring the unmapped * regions is wasteful. That said, because we can deal with small noises, * tracking every mapping is not strictly required but could even incur a high * overhead if the mapping frequently changes or the number of mappings is * high. The adaptive regions adjustment mechanism will further help to deal * with the noise by simply identifying the unmapped areas as a region that * has no access. Moreover, applying the real mappings that would have many * unmapped areas inside will make the adaptive mechanism quite complex. That * said, too huge unmapped areas inside the monitoring target should be removed * to not take the time for the adaptive mechanism. * * For the reason, we convert the complex mappings to three distinct regions * that cover every mapped area of the address space. Also the two gaps * between the three regions are the two biggest unmapped areas in the given * address space. In detail, this function first identifies the start and the * end of the mappings and the two biggest unmapped areas of the address space. * Then, it constructs the three regions as below: * * [mappings[0]->start, big_two_unmapped_areas[0]->start) * [big_two_unmapped_areas[0]->end, big_two_unmapped_areas[1]->start) * [big_two_unmapped_areas[1]->end, mappings[nr_mappings - 1]->end) * * As usual memory map of processes is as below, the gap between the heap and * the uppermost mmap()-ed region, and the gap between the lowermost mmap()-ed * region and the stack will be two biggest unmapped regions. Because these * gaps are exceptionally huge areas in usual address space, excluding these * two biggest unmapped regions will be sufficient to make a trade-off. * * <heap> * <BIG UNMAPPED REGION 1> * <uppermost mmap()-ed region> * (other mmap()-ed regions and small unmapped regions) * <lowermost mmap()-ed region> * <BIG UNMAPPED REGION 2> * <stack>
*/ staticvoid __damon_va_init_regions(struct damon_ctx *ctx, struct damon_target *t)
{ struct damon_target *ti; struct damon_region *r; struct damon_addr_range regions[3]; unsignedlong sz = 0, nr_pieces; int i, tidx = 0;
if (damon_va_three_regions(t, regions)) {
damon_for_each_target(ti, ctx) { if (ti == t) break;
tidx++;
}
pr_debug("Failed to get three regions of %dth target\n", tidx); return;
}
for (i = 0; i < 3; i++)
sz += regions[i].end - regions[i].start; if (ctx->attrs.min_nr_regions)
sz /= ctx->attrs.min_nr_regions; if (sz < DAMON_MIN_REGION)
sz = DAMON_MIN_REGION;
/* Set the initial three regions of the target */ for (i = 0; i < 3; i++) {
r = damon_new_region(regions[i].start, regions[i].end); if (!r) {
pr_err("%d'th init region creation failed\n", i); return;
}
damon_add_region(r, t);
/* * Check whether the region was accessed after the last preparation * * mm 'mm_struct' for the given virtual address space * r the region to be checked
*/ staticvoid __damon_va_check_access(struct mm_struct *mm, struct damon_region *r, bool same_target, struct damon_attrs *attrs)
{ staticunsignedlong last_addr; staticunsignedlong last_folio_sz = PAGE_SIZE; staticbool last_accessed;
if (!mm) {
damon_update_region_access_rate(r, false, attrs); return;
}
/* If the region is in the last checked page, reuse the result */ if (same_target && (ALIGN_DOWN(last_addr, last_folio_sz) ==
ALIGN_DOWN(r->sampling_addr, last_folio_sz))) {
damon_update_region_access_rate(r, last_accessed, attrs); return;
}
damos_for_each_ops_filter(filter, scheme) { /* * damos_folio_filter_match checks the young filter by doing an * rmap on the folio to find its page table. However, being the * vaddr scheme, we have direct access to the page tables, so * use that instead.
*/ if (filter->type == DAMOS_FILTER_TYPE_YOUNG)
matched = damos_va_filter_young_match(filter, folio,
vma, addr, ptep, pmdp); else
matched = damos_folio_filter_match(filter, folio);
if (matched) return !filter->allow;
} return scheme->ops_filters_default_reject;
}
/* * Place the given folio in the migration_list corresponding to where the folio * should be migrated. * * The algorithm used here is similar to weighted_interleave_nid()
*/ staticvoid damos_va_migrate_dests_add(struct folio *folio, struct vm_area_struct *vma, unsignedlong addr, struct damos_migrate_dests *dests, struct list_head *migration_lists)
{
pgoff_t ilx; int order; unsignedint target; unsignedint weight_total = 0; int i;
/* * If dests is empty, there is only one migration list corresponding * to s->target_nid.
*/ if (!dests->nr_dests) {
i = 0; goto isolate;
}
/* Don't set the monitoring target regions for the entire mapping */
ops_fvaddr.id = DAMON_OPS_FVADDR;
ops_fvaddr.init = NULL;
ops_fvaddr.update = NULL;
err = damon_register_ops(&ops); if (err) return err; return damon_register_ops(&ops_fvaddr);
};
subsys_initcall(damon_va_initcall);
#include"tests/vaddr-kunit.h"
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.