/* StarPU --- Runtime system for heterogeneous multicore architectures. * * Copyright (C) 2017-2020 Université de Bordeaux, CNRS (LaBRI UMR 5800), Inria * * StarPU is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or (at * your option) any later version. * * StarPU is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU Lesser General Public License in COPYING.LGPL for more details. */ /* CPUSET routines */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #ifdef HAVE_HWLOC_GLIBC_SCHED_H #include #endif #include #include #include #include #ifndef STARPURM_HAVE_DLB #error "STARPU-RM DLB support not enabled" #endif #include #include /* * DLB interfacing */ static dlb_handler_t dlb_handle; static cpu_set_t starpurm_process_mask; static hwloc_cpuset_t starpurm_process_cpuset; static struct s_starpurm *_starpurm = NULL; static pthread_mutex_t dlb_handle_mutex = PTHREAD_MUTEX_INITIALIZER; static int glibc_cpuid_to_unitid[CPU_SETSIZE]; static int *unitid_to_glibc_cpuid = NULL; static const char * _dlb_error_str(int error_code) { const char *s = NULL; switch (error_code) { case DLB_NOUPDT: s="DLB_NOUPDT"; break; case DLB_NOTED: s="DLB_NOTED"; break; case DLB_SUCCESS: s="DLB_SUCCESS"; break; case DLB_ERR_UNKNOWN: s="DLB_ERR_UNKNOWN"; break; case DLB_ERR_NOINIT: s="DLB_ERR_NOINIT"; break; case DLB_ERR_INIT: s="DLB_ERR_INIT"; break; case DLB_ERR_DISBLD: s="DLB_ERR_DISBLD"; break; case DLB_ERR_NOSHMEM: s="DLB_ERR_NOSHMEM"; break; case DLB_ERR_NOPROC: s="DLB_ERR_NOPROC"; break; case DLB_ERR_PDIRTY: s="DLB_ERR_PDIRTY"; break; case DLB_ERR_PERM: s="DLB_ERR_PERM"; break; case DLB_ERR_TIMEOUT: s="DLB_ERR_TIMEOUT"; break; case DLB_ERR_NOCBK: s="DLB_ERR_NOCBK"; break; case DLB_ERR_NOENT: s="DLB_ERR_NOENT"; break; case DLB_ERR_NOCOMP: s="DLB_ERR_NOCOMP"; break; case DLB_ERR_REQST: s="DLB_ERR_REQST"; break; case DLB_ERR_NOMEM: s="DLB_ERR_NOMEM"; break; case DLB_ERR_NOPOL: s="DLB_ERR_NOPOL"; break; default: s = ""; break; } return s; } #define _dlb_check(s,r) do { if ((r) != DLB_SUCCESS) {fprintf(stderr, "%s:%d, %s - DLB call '%s' %s %d (%s)\n",__FILE__, __LINE__, __func__, (s), (r)>0?"returned warning code":"failed with error code", (r), _dlb_error_str((r))); assert(dlb_ret >= DLB_SUCCESS); }} while (0) #if 0 /* unused for now */ static void _glibc_cpuset_to_hwloc_cpuset(const cpu_set_t *glibc_cpuset, hwloc_cpuset_t *hwloc_cpuset) { assert(_starpurm != NULL); assert(_starpurm->state != state_uninitialized); struct s_starpurm *rm = _starpurm; int status = hwloc_cpuset_from_glibc_sched_affinity(rm->topology, *hwloc_cpuset, glibc_cpuset, sizeof(cpu_set_t)); assert(status == 0); } #endif static void _hwloc_cpuset_to_glibc_cpuset(const hwloc_cpuset_t hwloc_cpuset, cpu_set_t *glibc_cpuset) { assert(_starpurm != NULL); assert(_starpurm->state != state_uninitialized); struct s_starpurm *rm = _starpurm; int status = hwloc_cpuset_to_glibc_sched_affinity(rm->topology, hwloc_cpuset, glibc_cpuset, sizeof(cpu_set_t)); assert(status == 0); } int starpurm_dlb_notify_starpu_worker_mask_going_to_sleep(const hwloc_cpuset_t hwloc_workers_cpuset) { int status = 0; pthread_mutex_lock(&dlb_handle_mutex); if (dlb_handle != NULL) { hwloc_cpuset_t hwloc_to_lend_cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t hwloc_to_return_cpuset = hwloc_bitmap_alloc(); hwloc_bitmap_zero(hwloc_to_lend_cpuset); hwloc_bitmap_zero(hwloc_to_return_cpuset); hwloc_bitmap_and(hwloc_to_lend_cpuset, hwloc_workers_cpuset, starpurm_process_cpuset); hwloc_bitmap_andnot(hwloc_to_return_cpuset, hwloc_workers_cpuset, starpurm_process_cpuset); #ifdef STARPURM_DLB_VERBOSE { char * s_to_lend = NULL; char * s_to_return = NULL; hwloc_bitmap_asprintf(&s_to_lend, hwloc_to_lend_cpuset); hwloc_bitmap_asprintf(&s_to_return, hwloc_to_return_cpuset); fprintf(stderr, "%s: to_lend='%s', to_return='%s'\n", __func__, s_to_lend, s_to_return); free(s_to_lend); free(s_to_return); } #endif if (!hwloc_bitmap_iszero(hwloc_to_lend_cpuset)) { cpu_set_t glibc_to_lend_cpuset; CPU_ZERO(&glibc_to_lend_cpuset); _hwloc_cpuset_to_glibc_cpuset(hwloc_to_lend_cpuset, &glibc_to_lend_cpuset); int dlb_ret = DLB_LendCpuMask_sp(dlb_handle, &glibc_to_lend_cpuset); _dlb_check("DLB_LendCpuMask_sp", dlb_ret); } if (!hwloc_bitmap_iszero(hwloc_to_return_cpuset)) { cpu_set_t glibc_to_return_cpuset; CPU_ZERO(&glibc_to_return_cpuset); _hwloc_cpuset_to_glibc_cpuset(hwloc_to_return_cpuset, &glibc_to_return_cpuset); /* Use DLB_Lend for returning borrowed units. DLB_Return seems to require that * a reclaim has previously been emitted by the unit owning runtime system */ #if 0 int dlb_ret = DLB_ReturnCpuMask_sp(dlb_handle, &glibc_to_return_cpuset); _dlb_check("DLB_ReturnCpuMask_sp", dlb_ret); #else int dlb_ret = DLB_LendCpuMask_sp(dlb_handle, &glibc_to_return_cpuset); _dlb_check("DLB_LendCpuMask_sp", dlb_ret); #endif } hwloc_bitmap_free(hwloc_to_lend_cpuset); hwloc_bitmap_free(hwloc_to_return_cpuset); status = 1; } pthread_mutex_unlock(&dlb_handle_mutex); return status; } int starpurm_dlb_notify_starpu_worker_mask_waking_up(const hwloc_cpuset_t hwloc_workers_cpuset) { int status = 0; pthread_mutex_lock(&dlb_handle_mutex); if (dlb_handle != NULL) { hwloc_cpuset_t hwloc_to_reclaim_cpuset = hwloc_bitmap_alloc(); hwloc_cpuset_t hwloc_to_borrow_cpuset = hwloc_bitmap_alloc(); hwloc_bitmap_zero(hwloc_to_reclaim_cpuset); hwloc_bitmap_zero(hwloc_to_borrow_cpuset); hwloc_bitmap_and(hwloc_to_reclaim_cpuset, hwloc_workers_cpuset, starpurm_process_cpuset); hwloc_bitmap_andnot(hwloc_to_borrow_cpuset, hwloc_workers_cpuset, starpurm_process_cpuset); #ifdef STARPURM_DLB_VERBOSE { char * s_to_reclaim = NULL; char * s_to_borrow = NULL; hwloc_bitmap_asprintf(&s_to_reclaim, hwloc_to_reclaim_cpuset); hwloc_bitmap_asprintf(&s_to_borrow, hwloc_to_borrow_cpuset); fprintf(stderr, "%s: to_reclaim='%s', to_borrow='%s'\n", __func__, s_to_reclaim, s_to_borrow); free(s_to_reclaim); free(s_to_borrow); } #endif if (!hwloc_bitmap_iszero(hwloc_to_reclaim_cpuset)) { cpu_set_t glibc_to_reclaim_cpuset; CPU_ZERO(&glibc_to_reclaim_cpuset); _hwloc_cpuset_to_glibc_cpuset(hwloc_to_reclaim_cpuset, &glibc_to_reclaim_cpuset); int dlb_ret = DLB_ReclaimCpuMask_sp(dlb_handle, &glibc_to_reclaim_cpuset); _dlb_check("DLB_ReclaimCpuMask_sp", dlb_ret); } if (!hwloc_bitmap_iszero(hwloc_to_borrow_cpuset)) { cpu_set_t glibc_to_borrow_cpuset; CPU_ZERO(&glibc_to_borrow_cpuset); _hwloc_cpuset_to_glibc_cpuset(hwloc_to_borrow_cpuset, &glibc_to_borrow_cpuset); int dlb_ret = DLB_BorrowCpuMask_sp(dlb_handle, &glibc_to_borrow_cpuset); _dlb_check("DLB_BorrowCpuMask_sp", dlb_ret); } hwloc_bitmap_free(hwloc_to_reclaim_cpuset); hwloc_bitmap_free(hwloc_to_borrow_cpuset); status = 1; } pthread_mutex_unlock(&dlb_handle_mutex); return status; } #ifdef STARPURM_STARPU_HAVE_WORKER_CALLBACKS #ifdef STARPURM_HAVE_DLB_CALLBACK_ARG static void _dlb_callback_enable_cpu(int cpuid, void *arg) #else static void _dlb_callback_enable_cpu(int cpuid) #endif { #ifdef STARPURM_HAVE_DLB_CALLBACK_ARG (void) arg; #endif int unitid = glibc_cpuid_to_unitid[cpuid]; #ifdef STARPURM_DLB_VERBOSE fprintf(stderr, "%s: cpuid=%d, unitid=%d\n", __func__, cpuid, unitid); #endif if (unitid != -1) { starpurm_enqueue_event_cpu_unit_available(unitid); } } #ifdef STARPURM_HAVE_DLB_CALLBACK_ARG static void _dlb_callback_disable_cpu(int cpuid, void *arg) #else static void _dlb_callback_disable_cpu(int cpuid) #endif { #ifdef STARPURM_HAVE_DLB_CALLBACK_ARG (void) arg; #endif int unitid = glibc_cpuid_to_unitid[cpuid]; #ifdef STARPURM_DLB_VERBOSE fprintf(stderr, "%s: cpuid=%d, unitid=%d\n", __func__, cpuid, unitid); #endif if (unitid != -1) { /* nothing */ } } #endif void starpurm_dlb_init(struct s_starpurm *rm) { _starpurm = rm; { int unitid; int cpuid; unitid_to_glibc_cpuid = malloc(rm->nunits * sizeof(*unitid_to_glibc_cpuid)); for (cpuid = 0; cpuidnunits; unitid++) { hwloc_cpuset_t unit_cpuset = starpurm_get_unit_cpuset(unitid); cpu_set_t unit_mask; CPU_ZERO(&unit_mask); _hwloc_cpuset_to_glibc_cpuset(unit_cpuset, &unit_mask); unitid_to_glibc_cpuid[unitid] = -1; for (cpuid = 0; cpuidselected_cpuset); hwloc_bitmap_and(starpurm_process_cpuset, starpurm_process_cpuset, rm->initially_owned_cpuset_mask); _hwloc_cpuset_to_glibc_cpuset(starpurm_process_cpuset, &starpurm_process_mask); #ifdef STARPURM_DLB_VERBOSE { char * s_reachable = NULL; char * s_initially_owned = NULL; hwloc_bitmap_asprintf(&s_reachable, rm->selected_cpuset); hwloc_bitmap_asprintf(&s_initially_owned, starpurm_process_cpuset); fprintf(stderr, "%s: StarPU reachable units='%s', StarPU initially owned units='%s'\n", __func__, s_reachable, s_initially_owned); free(s_reachable); free(s_initially_owned); } #endif pthread_mutex_lock(&dlb_handle_mutex); /* TODO: autodetect DLB policy according to DLB version */ #if 1 dlb_handle = DLB_Init_sp(0, &starpurm_process_mask, "--lewi=yes --drom=no --mode=async"); #else dlb_handle = DLB_Init_sp(0, &starpurm_process_mask, "--policy=new --drom=no --mode=async"); #endif /* cpu-based callbacks are mutually exclusive with mask-based callbacks, * we only register cpu-based callbacks */ int dlb_ret; #ifdef STARPURM_STARPU_HAVE_WORKER_CALLBACKS #ifdef STARPURM_HAVE_DLB_CALLBACK_ARG dlb_ret = DLB_CallbackSet_sp(dlb_handle, dlb_callback_disable_cpu, (dlb_callback_t)_dlb_callback_disable_cpu, NULL); _dlb_check("DLB_CallbackSet_sp", dlb_ret); dlb_ret = DLB_CallbackSet_sp(dlb_handle, dlb_callback_enable_cpu, (dlb_callback_t)_dlb_callback_enable_cpu, NULL); _dlb_check("DLB_CallbackSet_sp", dlb_ret); #else dlb_ret = DLB_CallbackSet_sp(dlb_handle, dlb_callback_disable_cpu, (dlb_callback_t)_dlb_callback_disable_cpu); _dlb_check("DLB_CallbackSet_sp", dlb_ret); dlb_ret = DLB_CallbackSet_sp(dlb_handle, dlb_callback_enable_cpu, (dlb_callback_t)_dlb_callback_enable_cpu); _dlb_check("DLB_CallbackSet_sp", dlb_ret); #endif #endif dlb_ret = DLB_Enable_sp(dlb_handle); _dlb_check("DLB_Enable_sp", dlb_ret); pthread_mutex_unlock(&dlb_handle_mutex); } void starpurm_dlb_exit(void) { pthread_mutex_lock(&dlb_handle_mutex); dlb_handler_t dlb_handle_save = dlb_handle; dlb_handle = 0; pthread_mutex_unlock(&dlb_handle_mutex); /* lend every resources that StarPU may still have */ DLB_Lend_sp(dlb_handle_save); DLB_Return_sp(dlb_handle_save); pthread_mutex_lock(&dlb_handle_mutex); DLB_Disable_sp(dlb_handle_save); DLB_Finalize_sp(dlb_handle_save); hwloc_bitmap_free(starpurm_process_cpuset); free(unitid_to_glibc_cpuid); pthread_mutex_unlock(&dlb_handle_mutex); }