/* * tell another cpu about a task to do and return only after the cpu acks that * the task is finished. Also wait before it finishes task sent by another cpu * to the same one. */ static void smp_schedule_sync(struct proc * p, unsigned task) { unsigned cpu = p->p_cpu; unsigned mycpu = cpuid; assert(cpu != mycpu); /* * if some other cpu made a request to the same cpu, wait until it is * done before proceeding */ if (sched_ipi_data[cpu].flags != 0) { BKL_UNLOCK(); while (sched_ipi_data[cpu].flags != 0) { if (sched_ipi_data[mycpu].flags) { BKL_LOCK(); smp_sched_handler(); BKL_UNLOCK(); } } BKL_LOCK(); } sched_ipi_data[cpu].data = (u32_t) p; sched_ipi_data[cpu].flags |= task; __insn_barrier(); arch_send_smp_schedule_ipi(cpu); /* wait until the destination cpu finishes its job */ BKL_UNLOCK(); while (sched_ipi_data[cpu].flags != 0) { if (sched_ipi_data[mycpu].flags) { BKL_LOCK(); smp_sched_handler(); BKL_UNLOCK(); } } BKL_LOCK(); }
PUBLIC void context_stop(struct proc * p) { u64_t tsc, tsc_delta; u64_t * __tsc_ctr_switch = get_cpulocal_var_ptr(tsc_ctr_switch); #ifdef CONFIG_SMP unsigned cpu = cpuid; /* * This function is called only if we switch from kernel to user or idle * or back. Therefore this is a perfect location to place the big kernel * lock which will hopefully disappear soon. * * If we stop accounting for KERNEL we must unlock the BKL. If account * for IDLE we must not hold the lock */ if (p == proc_addr(KERNEL)) { u64_t tmp; read_tsc_64(&tsc); tmp = sub64(tsc, *__tsc_ctr_switch); kernel_ticks[cpu] = add64(kernel_ticks[cpu], tmp); p->p_cycles = add64(p->p_cycles, tmp); BKL_UNLOCK(); } else { u64_t bkl_tsc; atomic_t succ; read_tsc_64(&bkl_tsc); /* this only gives a good estimate */ succ = big_kernel_lock.val; BKL_LOCK(); read_tsc_64(&tsc); bkl_ticks[cpu] = add64(bkl_ticks[cpu], sub64(tsc, bkl_tsc)); bkl_tries[cpu]++; bkl_succ[cpu] += !(!(succ == 0)); p->p_cycles = add64(p->p_cycles, sub64(tsc, *__tsc_ctr_switch)); #ifdef CONFIG_SMP /* * Since at the time we got a scheduling IPI we might have been * waiting for BKL already, we may miss it due to a similar IPI to * the cpu which is already waiting for us to handle its. This * results in a live-lock of these two cpus. * * Therefore we always check if there is one pending and if so, * we handle it straight away so the other cpu can continue and * we do not deadlock. */ smp_sched_handler(); #endif } #else read_tsc_64(&tsc); p->p_cycles = add64(p->p_cycles, sub64(tsc, *__tsc_ctr_switch)); #endif tsc_delta = sub64(tsc, *__tsc_ctr_switch); if(kbill_ipc) { kbill_ipc->p_kipc_cycles = add64(kbill_ipc->p_kipc_cycles, tsc_delta); kbill_ipc = NULL; } if(kbill_kcall) { kbill_kcall->p_kcall_cycles = add64(kbill_kcall->p_kcall_cycles, tsc_delta); kbill_kcall = NULL; } /* * deduct the just consumed cpu cycles from the cpu time left for this * process during its current quantum. Skip IDLE and other pseudo kernel * tasks */ if (p->p_endpoint >= 0) { #if DEBUG_RACE make_zero64(p->p_cpu_time_left); #else /* if (tsc_delta < p->p_cpu_time_left) in 64bit */ if (ex64hi(tsc_delta) < ex64hi(p->p_cpu_time_left) || (ex64hi(tsc_delta) == ex64hi(p->p_cpu_time_left) && ex64lo(tsc_delta) < ex64lo(p->p_cpu_time_left))) p->p_cpu_time_left = sub64(p->p_cpu_time_left, tsc_delta); else { make_zero64(p->p_cpu_time_left); } #endif } *__tsc_ctr_switch = tsc; }