/* * Select a gru fault map to be used by the current cpu. Note that * multiple cpus may be using the same map. * ZZZ should be inline but did not work on emulator
*/ int gru_cpu_fault_map_id(void)
{ int cpu = smp_processor_id(); int id, core;
/*--------- ASID Management ------------------------------------------- * * Initially, assign asids sequentially from MIN_ASID .. MAX_ASID. * Once MAX is reached, flush the TLB & start over. However, * some asids may still be in use. There won't be many (percentage wise) still * in use. Search active contexts & determine the value of the first * asid in use ("x"s below). Set "limit" to this value. * This defines a block of assignable asids. * * When "limit" is reached, search forward from limit+1 and determine the * next block of assignable asids. * * Repeat until MAX_ASID is reached, then start over again. * * Each time MAX_ASID is reached, increment the asid generation. Since * the search for in-use asids only checks contexts with GRUs currently * assigned, asids in some contexts will be missed. Prior to loading * a context, the asid generation of the GTS asid is rechecked. If it * doesn't match the current generation, a new asid will be assigned. * * 0---------------x------------x---------------------x----| * ^-next ^-limit ^-MAX_ASID * * All asid manipulation & context loading/unloading is protected by the * gs_lock.
*/
/* Hit the asid limit. Start over */ staticint gru_wrap_asid(struct gru_state *gru)
{
gru_dbg(grudev, "gid %d\n", gru->gs_gid);
STAT(asid_wrap);
gru->gs_asid_gen++; return MIN_ASID;
}
/* Find the next chunk of unused asids */ staticint gru_reset_asid_limit(struct gru_state *gru, int asid)
{ int i, gid, inuse_asid, limit;
gru_dbg(grudev, "gid %d, asid 0x%x\n", gru->gs_gid, asid);
STAT(asid_next);
limit = MAX_ASID; if (asid >= limit)
asid = gru_wrap_asid(gru);
gru_flush_all_tlb(gru);
gid = gru->gs_gid;
again: for (i = 0; i < GRU_NUM_CCH; i++) { if (!gru->gs_gts[i] || is_kernel_context(gru->gs_gts[i])) continue;
inuse_asid = gru->gs_gts[i]->ts_gms->ms_asids[gid].mt_asid;
gru_dbg(grudev, "gid %d, gts %p, gms %p, inuse 0x%x, cxt %d\n",
gru->gs_gid, gru->gs_gts[i], gru->gs_gts[i]->ts_gms,
inuse_asid, i); if (inuse_asid == asid) {
asid += ASID_INC; if (asid >= limit) { /* * empty range: reset the range limit and * start over
*/
limit = MAX_ASID; if (asid >= MAX_ASID)
asid = gru_wrap_asid(gru); goto again;
}
}
/* * Clear n bits in a word. Return a word indicating the bits that were cleared. * Optionally, build an array of chars that contain the bit numbers allocated.
*/ staticunsignedlong reserve_resources(unsignedlong *p, int n, int mmax, signedchar *idx)
{ unsignedlong bits = 0; int i;
while (n--) {
i = find_first_bit(p, mmax); if (i == mmax)
BUG();
__clear_bit(i, p);
__set_bit(i, &bits); if (idx)
*idx++ = i;
} return bits;
}
/* * Check if a GRU has sufficient free resources to satisfy an allocation * request. Note: GRU locks may or may not be held when this is called. If * not held, recheck after acquiring the appropriate locks. * * Returns 1 if sufficient resources, 0 if not
*/ staticint check_gru_resources(struct gru_state *gru, int cbr_au_count, int dsr_au_count, int max_active_contexts)
{ return hweight64(gru->gs_cbr_map) >= cbr_au_count
&& hweight64(gru->gs_dsr_map) >= dsr_au_count
&& gru->gs_active_contexts < max_active_contexts;
}
/* * TLB manangment requires tracking all GRU chiplets that have loaded a GSEG * context.
*/ staticint gru_load_mm_tracker(struct gru_state *gru, struct gru_thread_state *gts)
{ struct gru_mm_struct *gms = gts->ts_gms; struct gru_mm_tracker *asids = &gms->ms_asids[gru->gs_gid]; unsignedshort ctxbitmap = (1 << gts->ts_ctxnum); int asid;
/* * Decrement the reference count on a GTS structure. Free the structure * if the reference count goes to zero.
*/ void gts_drop(struct gru_thread_state *gts)
{ if (gts && refcount_dec_and_test(>s->ts_refcnt)) { if (gts->ts_gms)
gru_drop_mmu_notifier(gts->ts_gms);
kfree(gts);
STAT(gts_free);
}
}
/* * Locate the GTS structure for the current thread.
*/ staticstruct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data
*vdata, int tsid)
{ struct gru_thread_state *gts;
/* * Allocate a thread state structure.
*/ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma, int cbr_au_count, int dsr_au_count, unsignedchar tlb_preload_count, int options, int tsid)
{ struct gru_thread_state *gts; struct gru_mm_struct *gms; int bytes;
/* * Find the thread state structure for the current thread.
*/ struct gru_thread_state *gru_find_thread_state(struct vm_area_struct *vma, int tsid)
{ struct gru_vma_data *vdata = vma->vm_private_data; struct gru_thread_state *gts;
/* * Allocate a new thread state for a GSEG. Note that races may allow * another thread to race to create a gts.
*/ struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma, int tsid)
{ struct gru_vma_data *vdata = vma->vm_private_data; struct gru_thread_state *gts, *ngts;
/* * Prefetching cachelines help hardware performance. * (Strictly a performance enhancement. Not functionally required).
*/ staticvoid prefetch_data(void *p, int num, int stride)
{ while (num-- > 0) {
prefetchw(p);
p += stride;
}
}
/* CBEs may not be coherent. Flush them from cache */
for_each_cbr_in_allocation_map(i, &cbrmap, scr)
gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
mb(); /* Let the CL flush complete */
if (!is_kernel_context(gts))
gru_unload_mm_tracker(gru, gts); if (savestate) {
gru_unload_context_data(gts->ts_gdata, gru->gs_gru_base_vaddr,
ctxnum, gts->ts_cbr_map,
gts->ts_dsr_map);
gts->ts_data_valid = 1;
}
if (cch_deallocate(cch))
BUG();
unlock_cch_handle(cch);
gru_free_gru_context(gts);
}
/* * Load a GRU context by copying it from the thread data structure in memory * to the GRU.
*/ void gru_load_context(struct gru_thread_state *gts)
{ struct gru_state *gru = gts->ts_gru; struct gru_context_configuration_handle *cch; int i, err, asid, ctxnum = gts->ts_ctxnum;
/* * Update fields in an active CCH: * - retarget interrupts on local blade * - update sizeavail mask
*/ int gru_update_cch(struct gru_thread_state *gts)
{ struct gru_context_configuration_handle *cch; struct gru_state *gru = gts->ts_gru; int i, ctxnum = gts->ts_ctxnum, ret = 0;
cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
lock_cch_handle(cch); if (cch->state == CCHSTATE_ACTIVE) { if (gru->gs_gts[gts->ts_ctxnum] != gts) gotoexit; if (cch_interrupt(cch))
BUG(); for (i = 0; i < 8; i++)
cch->sizeavail[i] = gts->ts_sizeavail;
gts->ts_tlb_int_select = gru_cpu_fault_map_id();
cch->tlb_int_select = gru_cpu_fault_map_id();
cch->tfm_fault_bit_enable =
(gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
|| gts->ts_user_options == GRU_OPT_MISS_FMM_INTR); if (cch_start(cch))
BUG();
ret = 1;
} exit:
unlock_cch_handle(cch); return ret;
}
/* * Update CCH tlb interrupt select. Required when all the following is true: * - task's GRU context is loaded into a GRU * - task is using interrupt notification for TLB faults * - task has migrated to a different cpu on the same blade where * it was previously running.
*/ staticint gru_retarget_intr(struct gru_thread_state *gts)
{ if (gts->ts_tlb_int_select < 0
|| gts->ts_tlb_int_select == gru_cpu_fault_map_id()) return 0;
gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select,
gru_cpu_fault_map_id()); return gru_update_cch(gts);
}
/* * Check if a GRU context is allowed to use a specific chiplet. By default * a context is assigned to any blade-local chiplet. However, users can * override this. * Returns 1 if assignment allowed, 0 otherwise
*/ staticint gru_check_chiplet_assignment(struct gru_state *gru, struct gru_thread_state *gts)
{ int blade_id; int chiplet_id;
blade_id = gts->ts_user_blade_id; if (blade_id < 0)
blade_id = uv_numa_blade_id();
/* * Unload the gru context if it is not assigned to the correct blade or * chiplet. Misassignment can occur if the process migrates to a different * blade or if the user changes the selected blade/chiplet.
*/ int gru_check_context_placement(struct gru_thread_state *gts)
{ struct gru_state *gru; int ret = 0;
/* * If the current task is the context owner, verify that the * context is correctly placed. This test is skipped for non-owner * references. Pthread apps use non-owner references to the CBRs.
*/
gru = gts->ts_gru; /* * If gru or gts->ts_tgid_owner isn't initialized properly, return * success to indicate that the caller does not need to unload the * gru context.The caller is responsible for their inspection and * reinitialization if needed.
*/ if (!gru || gts->ts_tgid_owner != current->tgid) return ret;
if (!gru_check_chiplet_assignment(gru, gts)) {
STAT(check_context_unload);
ret = -EINVAL;
} elseif (gru_retarget_intr(gts)) {
STAT(check_context_retarget_intr);
}
return ret;
}
/* * Insufficient GRU resources available on the local blade. Steal a context from * a process. This is a hack until a _real_ resource scheduler is written....
*/ #define next_ctxnum(n) ((n) < GRU_NUM_CCH - 2 ? (n) + 1 : 0) #define next_gru(b, g) (((g) < &(b)->bs_grus[GRU_CHIPLETS_PER_BLADE - 1]) ? \
((g)+1) : &(b)->bs_grus[0])
/* * Scan the GRUs on the local blade & assign a GRU context.
*/ struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts)
{ struct gru_state *gru, *grux; int i, max_active_contexts; int blade_id = gts->ts_user_blade_id;
/* The following check ensures vaddr is a valid address in the VMA */
gts = gru_find_thread_state(vma, TSID(vaddr, vma)); if (!gts) return VM_FAULT_SIGBUS;
again:
mutex_lock(>s->ts_ctxlock);
if (gru_check_context_placement(gts)) {
mutex_unlock(>s->ts_ctxlock);
gru_unload_context(gts, 1); return VM_FAULT_NOPAGE;
}
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.