// Copyright(c) 2018-2020, Intel Corporation // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of Intel Corporation nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #include #include #include #include #include #include "monitored_device.h" #include "monitor_thread.h" #include "api/sysfs.h" #ifdef LOG #undef LOG #endif #define LOG(format, ...) \ log_printf("monitored_device: " format, ##__VA_ARGS__) fpgad_supported_device default_supported_devices_table[] = { { 0x8086, 0xbcc0, "libfpgad-xfpga.so", 0, NULL, "" }, { 0x8086, 0xbcc1, "libfpgad-xfpga.so", 0, NULL, "" }, { 0x8086, 0x0b30, "libfpgad-vc.so", 0, NULL, "" }, { 0x8086, 0x0b31, "libfpgad-vc.so", 0, NULL, "" }, { 0, 0, NULL, 0, NULL, "" }, }; STATIC fpgad_supported_device *mon_is_loaded(struct fpgad_config *c, const char *library_path) { unsigned i; int res = 0; for (i = 0 ; c->supported_devices[i].library_path ; ++i) { fpgad_supported_device *d = &c->supported_devices[i]; res = strcmp(library_path, d->library_path); if (!res && (d->flags & FPGAD_DEV_LOADED)) return d; } return NULL; } STATIC fpgad_monitored_device * allocate_monitored_device(struct fpgad_config *config, fpgad_supported_device *supported, fpga_token token, uint64_t object_id, fpga_objtype object_type, opae_bitstream_info *bitstr) { fpgad_monitored_device *d; d = (fpgad_monitored_device *) calloc( 1, sizeof(fpgad_monitored_device)); if (!d) { LOG("out of memory"); return NULL; } d->config = config; d->supported = supported; d->token = token; d->object_id = object_id; d->object_type = object_type; d->bitstr = bitstr; return d; } STATIC void *mon_find_plugin(const char *libpath) { char plugin_path[PATH_MAX]; const char *search_paths[] = { OPAE_MODULE_SEARCH_PATHS }; unsigned i; void *dl_handle; for (i = 0 ; i < sizeof(search_paths) / sizeof(search_paths[0]) ; ++i) { snprintf(plugin_path, sizeof(plugin_path), "%s%s", search_paths[i], libpath); dl_handle = dlopen(plugin_path, RTLD_LAZY|RTLD_LOCAL); if (dl_handle) return dl_handle; } return NULL; } STATIC bool mon_consider_device(struct fpgad_config *c, fpga_token token) { unsigned i; fpga_properties props = NULL; fpga_token parent = NULL; fpga_properties parent_props = NULL; fpga_result res; uint16_t vendor_id; uint16_t device_id; uint64_t object_id; fpga_objtype object_type; opae_bitstream_info *bitstr = NULL; fpga_guid pr_ifc_id; bool added = false; res = fpgaGetProperties(token, &props); if (res != FPGA_OK) { LOG("failed to get properties\n"); return false; } vendor_id = 0; res = fpgaPropertiesGetVendorID(props, &vendor_id); if (res != FPGA_OK) { LOG("failed to get vendor ID\n"); goto err_out_destroy; } device_id = 0; res = fpgaPropertiesGetDeviceID(props, &device_id); if (res != FPGA_OK) { LOG("failed to get device ID\n"); goto err_out_destroy; } object_id = 0; res = fpgaPropertiesGetObjectID(props, &object_id); if (res != FPGA_OK) { LOG("failed to get object ID\n"); goto err_out_destroy; } object_type = FPGA_ACCELERATOR; res = fpgaPropertiesGetObjectType(props, &object_type); if (res != FPGA_OK) { LOG("failed to get object type\n"); goto err_out_destroy; } // Do we have a NULL GBS from the command line // that matches this device? if (object_type == FPGA_DEVICE) { // The token's guid is the PR interface ID. res = fpgaPropertiesGetGUID(props, &pr_ifc_id); if (res != FPGA_OK) { LOG("failed to get PR interface ID\n");\ goto err_out_destroy; } for (i = 0 ; i < c->num_null_gbs ; ++i) { if (!uuid_compare(c->null_gbs[i].pr_interface_id, pr_ifc_id)) { bitstr = &c->null_gbs[i]; break; } } } else { // The parent token's guid is the PR interface ID. res = fpgaPropertiesGetParent(props, &parent); if (res != FPGA_OK) { LOG("failed to get parent token\n"); goto err_out_destroy; } res = fpgaGetProperties(parent, &parent_props); if (res != FPGA_OK) { LOG("failed to get parent properties\n"); goto err_out_destroy; } res = fpgaPropertiesGetGUID(parent_props, &pr_ifc_id); if (res != FPGA_OK) { LOG("failed to get PR interface ID\n"); goto err_out_destroy; } fpgaDestroyProperties(&parent_props); fpgaDestroyToken(&parent); for (i = 0 ; i < c->num_null_gbs ; ++i) { if (!uuid_compare(c->null_gbs[i].pr_interface_id, pr_ifc_id)) { bitstr = &c->null_gbs[i]; break; } } } fpgaDestroyProperties(&props); for (i = 0 ; c->supported_devices[i].library_path ; ++i) { fpgad_supported_device *d = &c->supported_devices[i]; // Do we support this device? if (d->vendor_id == vendor_id && d->device_id == device_id) { fpgad_supported_device *loaded_by; fpgad_monitored_device *monitored; fpgad_plugin_configure_t cfg; int res; d->flags |= FPGAD_DEV_DETECTED; // Is the fpgad plugin already loaded? loaded_by = mon_is_loaded(c, d->library_path); if (loaded_by) { // The two table entries will share the // same plugin handle (but only loaded_by // will have FPGAD_DEV_LOADED). d->dl_handle = loaded_by->dl_handle; } else { // Plugin hasn't been loaded. // Load it now. d->dl_handle = mon_find_plugin(d->library_path); if (!d->dl_handle) { char *err = dlerror(); LOG("failed to load \"%s\" %s\n", d->library_path, err ? err : ""); continue; } d->flags |= FPGAD_DEV_LOADED; } if (!bitstr) { LOG("Warning: no NULL GBS for vid=0x%04x " "did=0x%04x objid=0x%x (%s)\n", vendor_id, device_id, object_id, object_type == FPGA_ACCELERATOR ? "accelerator" : "device"); } // Add the device to the monitored list. monitored = allocate_monitored_device(c, d, token, object_id, object_type, bitstr); if (!monitored) { LOG("failed to add device 0x%04x:0x%04x\n", vendor_id, device_id); continue; } // Success cfg = (fpgad_plugin_configure_t) dlsym(d->dl_handle, FPGAD_PLUGIN_CONFIGURE); if (!cfg) { LOG("failed to find %s in \"%s\"\n", FPGAD_PLUGIN_CONFIGURE, d->library_path); free(monitored); continue; } res = cfg(monitored, d->config); if (res) { LOG("%s for \"%s\" returned %d.\n", FPGAD_PLUGIN_CONFIGURE, d->library_path, res); free(monitored); continue; } if (monitored->type == FPGAD_PLUGIN_TYPE_THREAD) { if (monitored->thread_fn) { if (pthread_create(&monitored->thread, NULL, monitored->thread_fn, monitored)) { LOG("failed to create thread" " for \"%s\"\n", d->library_path); free(monitored); continue; } } else { LOG("Thread plugin \"%s\" has no " "thread_fn\n", d->library_path); free(monitored); continue; } } mon_monitor_device(monitored); added = true; break; } } return added; err_out_destroy: if (props) fpgaDestroyProperties(&props); if (parent) fpgaDestroyToken(&parent); if (parent_props) fpgaDestroyProperties(&parent_props); return false; } int mon_enumerate(struct fpgad_config *c) { fpga_token *tokens = NULL; fpga_result res; uint32_t num_matches = 0; uint32_t i; unsigned monitored_devices = 0; res = fpgaEnumerate(NULL, 0, NULL, 0, &num_matches); if (res != FPGA_OK) { LOG("enumeration failed\n"); return res; } if (!num_matches) { res = 1; return res; } tokens = calloc(num_matches, sizeof(fpga_token)); if (!tokens) { res = 1; LOG("out of memory\n"); return res; } res = fpgaEnumerate(NULL, 0, tokens, num_matches, &num_matches); if (res != FPGA_OK) { LOG("enumeration failed (2)\n"); goto out_exit; } for (i = 0 ; i < num_matches ; ++i) { if (!mon_consider_device(c, tokens[i])) { // Not monitoring it. Destroy the token. fpgaDestroyToken(&tokens[i]); } else { ++monitored_devices; } } out_exit: if (tokens) free(tokens); return res + (monitored_devices ? 0 : 1); }