📄 ml_sn_intr.c
字号:
*/static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit, /* Bit relative to the register */ hubreg_t **intpend_masks, /* Masks for this register */ intr_vecblk_t **vecblk, /* Vecblock for this interrupt */ int *ip) /* Which intpend register */{ hub_intmasks_t *hub_intmasks; synergy_da_t *sda; int which_synergy; cnodeid_t cnode; ASSERT(bit < N_INTPEND_BITS * 2); cnode = cpuid_to_cnodeid(cpu); which_synergy = cpuid_to_synergy(cpu); sda = Synergy_da_indr[(cnode * 2) + which_synergy]; hub_intmasks = &sda->s_intmasks; // hub_intmasks = &pdaindr[cpu].pda->p_intmasks; if (bit < N_INTPEND_BITS) { *intpend_masks = hub_intmasks->intpend0_masks; *vecblk = hub_intmasks->dispatch0; *ip = 0; *new_bit = bit; } else { *intpend_masks = hub_intmasks->intpend1_masks; *vecblk = hub_intmasks->dispatch1; *ip = 1; *new_bit = bit - N_INTPEND_BITS; } return hub_intmasks;}/* * intr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel, * intr_func_t intr_func, void *intr_arg); * This is the lowest-level interface to the interrupt code. It shouldn't * be called from outside the ml/SN directory. * intr_connect_level hooks up an interrupt to a particular bit in * the INT_PEND0/1 masks. Returns 0 on success. * cpu is the CPU to which the interrupt will be sent. * bit is the level bit to connect to * intr_swlevel tells which software level to use * intr_func is the interrupt handler * intr_arg is an arbitrary argument interpreted by the handler * intr_prefunc is a prologue function, to be called * with interrupts disabled, to disable * the interrupt at source. It is called * with the same argument. Should be NULL for * typical interrupts, which can be masked * by the infrastructure at the level bit. * intr_connect_level returns 0 on success or nonzero on an error *//* ARGSUSED */intintr_connect_level(cpuid_t cpu, int bit, ilvl_t intr_swlevel, intr_func_t intr_prefunc){ intr_vecblk_t *vecblk; hubreg_t *intpend_masks; int rv = 0; int ip; unsigned long s; ASSERT(bit < N_INTPEND_BITS * 2); (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &vecblk, &ip); INTR_LOCK(vecblk); if ((vecblk->info[bit].ii_flags & II_INUSE) || (!(vecblk->info[bit].ii_flags & II_RESERVE))) { /* Can't assign to a level that's in use or isn't reserved. */ rv = -1; } else { /* Stuff parameters into vector and info */ vecblk->vectors[bit].iv_prefunc = intr_prefunc; vecblk->info[bit].ii_flags |= II_INUSE; } /* Now stuff the masks if everything's okay. */ if (!rv) { int lslice; volatile hubreg_t *mask_reg; // nasid_t nasid = COMPACT_TO_NASID_NODEID(cpuid_to_cnodeid(cpu)); nasid_t nasid = cpuid_to_nasid(cpu); int subnode = cpuid_to_subnode(cpu); /* Make sure it's not already pending when we connect it. */ REMOTE_HUB_PI_CLR_INTR(nasid, subnode, bit + ip * N_INTPEND_BITS); if (bit >= GFX_INTR_A && bit <= CC_PEND_B) { intpend_masks[0] |= (1ULL << (uint64_t)bit); } lslice = cpuid_to_localslice(cpu); vecblk->cpu_count[lslice]++;#if SN1 /* * On SN1, there are 8 interrupt mask registers per node: * PI_0 MASK_0 A * PI_0 MASK_1 A * PI_0 MASK_0 B * PI_0 MASK_1 B * PI_1 MASK_0 A * PI_1 MASK_1 A * PI_1 MASK_0 B * PI_1 MASK_1 B */#endif if (ip == 0) { mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode, PI_INT_MASK0_A + PI_INT_MASK_OFFSET * lslice); } else { mask_reg = REMOTE_HUB_PI_ADDR(nasid, subnode, PI_INT_MASK1_A + PI_INT_MASK_OFFSET * lslice); } HUB_S(mask_reg, intpend_masks[0]); } INTR_UNLOCK(vecblk); return rv;}/* * intr_disconnect_level(cpuid_t cpu, int bit) * * This is the lowest-level interface to the interrupt code. It should * not be called from outside the ml/SN directory. * intr_disconnect_level removes a particular bit from an interrupt in * the INT_PEND0/1 masks. Returns 0 on success or nonzero on failure. */intintr_disconnect_level(cpuid_t cpu, int bit){ intr_vecblk_t *vecblk; hubreg_t *intpend_masks; unsigned long s; int rv = 0; int ip; (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &vecblk, &ip); INTR_LOCK(vecblk); if ((vecblk->info[bit].ii_flags & (II_RESERVE | II_INUSE)) != ((II_RESERVE | II_INUSE))) { /* Can't remove a level that's not in use or isn't reserved. */ rv = -1; } else { /* Stuff parameters into vector and info */ vecblk->vectors[bit].iv_func = (intr_func_t)NULL; vecblk->vectors[bit].iv_prefunc = (intr_func_t)NULL; vecblk->vectors[bit].iv_arg = 0; vecblk->info[bit].ii_flags &= ~II_INUSE;#ifdef BASE_ITHRTEAD vecblk->vectors[bit].iv_mustruncpu = -1; /* No mustrun CPU any more. */#endif } /* Now clear the masks if everything's okay. */ if (!rv) { int lslice; volatile hubreg_t *mask_reg; intpend_masks[0] &= ~(1ULL << (uint64_t)bit); lslice = cpuid_to_localslice(cpu); vecblk->cpu_count[lslice]--; mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cpuid_to_cnodeid(cpu)), cpuid_to_subnode(cpu), ip == 0 ? PI_INT_MASK0_A : PI_INT_MASK1_A); mask_reg = (volatile hubreg_t *)((__psunsigned_t)mask_reg + (PI_INT_MASK_OFFSET * lslice)); *mask_reg = intpend_masks[0]; } INTR_UNLOCK(vecblk); return rv;}/* * Actually block or unblock an interrupt */voiddo_intr_block_bit(cpuid_t cpu, int bit, int block){ intr_vecblk_t *vecblk; int ip; unsigned long s; hubreg_t *intpend_masks; volatile hubreg_t mask_value; volatile hubreg_t *mask_reg; intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &vecblk, &ip); INTR_LOCK(vecblk); if (block) /* Block */ intpend_masks[0] &= ~(1ULL << (uint64_t)bit); else /* Unblock */ intpend_masks[0] |= (1ULL << (uint64_t)bit); if (ip == 0) { mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cpuid_to_cnodeid(cpu)), cpuid_to_subnode(cpu), PI_INT_MASK0_A); } else { mask_reg = REMOTE_HUB_PI_ADDR(COMPACT_TO_NASID_NODEID(cpuid_to_cnodeid(cpu)), cpuid_to_subnode(cpu), PI_INT_MASK1_A); } HUB_S(mask_reg, intpend_masks[0]); /* * Wait for it to take effect. (One read should suffice.) * This is only necessary when blocking an interrupt */ if (block) while ((mask_value = HUB_L(mask_reg)) != intpend_masks[0]) ; INTR_UNLOCK(vecblk);}/* * Block a particular interrupt (cpu/bit pair). *//* ARGSUSED */voidintr_block_bit(cpuid_t cpu, int bit){ do_intr_block_bit(cpu, bit, 1);}/* * Unblock a particular interrupt (cpu/bit pair). *//* ARGSUSED */voidintr_unblock_bit(cpuid_t cpu, int bit){ do_intr_block_bit(cpu, bit, 0);}/* verifies that the specified CPUID is on the specified SUBNODE (if any) */#define cpu_on_subnode(cpuid, which_subnode) \ (((which_subnode) == SUBNODE_ANY) || (cpuid_to_subnode(cpuid) == (which_subnode)))/* * Choose one of the CPUs on a specified node or subnode to receive * interrupts. Don't pick a cpu which has been specified as a NOINTR cpu. * * Among all acceptable CPUs, the CPU that has the fewest total number * of interrupts targetted towards it is chosen. Note that we never * consider how frequent each of these interrupts might occur, so a rare * hardware error interrupt is weighted equally with a disk interrupt. */static cpuid_tdo_intr_cpu_choose(cnodeid_t cnode, int which_subnode){ cpuid_t cpu, best_cpu = CPU_NONE; int slice, min_count=1000; min_count = 1000; for (slice=0; slice < CPUS_PER_NODE; slice++) { intr_vecblk_t *vecblk0, *vecblk1; int total_intrs_to_slice; subnode_pda_t *snpda; int local_cpu_num; cpu = cnode_slice_to_cpuid(cnode, slice); if (cpu == CPU_NONE) continue; /* If this cpu isn't enabled for interrupts, skip it */ if (!cpu_enabled(cpu) || !cpu_allows_intr(cpu)) continue; /* If this isn't the right subnode, skip it */ if (!cpu_on_subnode(cpu, which_subnode)) continue; /* OK, this one's a potential CPU for interrupts */ snpda = SUBNODEPDA(cnode,SUBNODE(slice)); vecblk0 = &snpda->intr_dispatch0; vecblk1 = &snpda->intr_dispatch1; local_cpu_num = LOCALCPU(slice); total_intrs_to_slice = vecblk0->cpu_count[local_cpu_num] + vecblk1->cpu_count[local_cpu_num]; if (min_count > total_intrs_to_slice) { min_count = total_intrs_to_slice; best_cpu = cpu; } } return best_cpu;}/* * Choose an appropriate interrupt target CPU on a specified node. * If which_subnode is SUBNODE_ANY, then subnode is not considered. * Otherwise, the chosen CPU must be on the specified subnode. */static cpuid_tintr_cpu_choose_from_node(cnodeid_t cnode, int which_subnode){ return(do_intr_cpu_choose(cnode, which_subnode));}/* Make it easy to identify subnode vertices in the hwgraph */voidmark_subnodevertex_as_subnode(devfs_handle_t vhdl, int which_subnode){ graph_error_t rv; ASSERT(0 <= which_subnode); ASSERT(which_subnode < NUM_SUBNODES); rv = hwgraph_info_add_LBL(vhdl, INFO_LBL_CPUBUS, (arbitrary_info_t)which_subnode); ASSERT_ALWAYS(rv == GRAPH_SUCCESS); rv = hwgraph_info_export_LBL(vhdl, INFO_LBL_CPUBUS, sizeof(arbitrary_info_t)); ASSERT_ALWAYS(rv == GRAPH_SUCCESS);}/* * Given a device descriptor, extract interrupt target information and * choose an appropriate CPU. Return CPU_NONE if we can't make sense * out of the target information. * TBD: Should this be considered platform-independent code? *//* * intr_bit_reserve_test(cpuid,which_subnode,cnode,req_bit,intr_resflags, * owner_dev,intr_name,*resp_bit) * Either cpuid is not CPU_NONE or cnodeid not CNODE_NONE but * not both. * 1. If cpuid is specified, this routine tests if this cpu can be a valid * interrupt target candidate. * 2. If cnodeid is specified, this routine tests if there is a cpu on * this node which can be a valid interrupt target candidate. * 3. If a valid interrupt target cpu candidate is found then an attempt at * reserving an interrupt bit on the corresponding cnode is made. * * If steps 1 & 2 both fail or step 3 fails then we are not able to get a valid * interrupt target cpu then routine returns CPU_NONE (failure) * Otherwise routine returns cpuid of interrupt target (success) */static cpuid_tintr_bit_reserve_test(cpuid_t cpuid, int favor_subnode, cnodeid_t cnodeid, int req_bit, int intr_resflags, devfs_handle_t owner_dev, char *intr_name, int *resp_bit){ ASSERT((cpuid==CPU_NONE) || (cnodeid==CNODEID_NONE)); if (cnodeid != CNODEID_NONE) { /* Try to choose a interrupt cpu candidate */ cpuid = intr_cpu_choose_from_node(cnodeid, favor_subnode); } if (cpuid != CPU_NONE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -