// Copyright(c) 2018-2019, 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 "monitored_device.h" #include "monitor_thread.h" #include "event_dispatcher_thread.h" #ifdef LOG #undef LOG #endif #define LOG(format, ...) \ log_printf("monitor_thread: " format, ##__VA_ARGS__) monitor_thread_config monitor_config = { .global = &global_config, .sched_policy = SCHED_RR, .sched_priority = 20, }; STATIC pthread_mutex_t mon_list_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; STATIC fpgad_monitored_device *monitored_device_list; STATIC void mon_queue_response(fpgad_detection_status status, fpgad_respond_event_t response, fpgad_monitored_device *d, void *response_context) { if (status == FPGAD_STATUS_DETECTED_HIGH) { if (evt_queue_response_high(response, d, response_context)) { pthread_yield(); } else { LOG("high priority event queue is full. Dropping!\n"); } } else if (status == FPGAD_STATUS_DETECTED) { if (evt_queue_response(response, d, response_context)) { pthread_yield(); } else { LOG("event queue is full. Dropping!\n"); } } } STATIC void mon_monitor(fpgad_monitored_device *d) { unsigned i; if (!d->detections) return; for (i = 0 ; d->detections[i] ; ++i) { fpgad_detection_status result; fpgad_detect_event_t detect = d->detections[i]; void *detect_context = d->detection_contexts ? d->detection_contexts[i] : NULL; result = detect(d, detect_context); if (result != FPGAD_STATUS_NOT_DETECTED && d->responses) { fpgad_respond_event_t response = d->responses[i]; void *response_context = d->response_contexts ? d->response_contexts[i] : NULL; if (response) { mon_queue_response(result, response, d, response_context); } } } } STATIC volatile bool mon_is_ready = false; bool monitor_is_ready(void) { return mon_is_ready; } void *monitor_thread(void *thread_context) { monitor_thread_config *c = (monitor_thread_config *)thread_context; struct sched_param sched_param; int policy = 0; int res; int err; fpgad_monitored_device *d; LOG("starting\n"); res = pthread_getschedparam(pthread_self(), &policy, &sched_param); if (res) { LOG("error getting scheduler params: %s\n", strerror(res)); } else { policy = c->sched_policy; sched_param.sched_priority = c->sched_priority; res = pthread_setschedparam(pthread_self(), policy, &sched_param); if (res) { LOG("error setting scheduler params" " (got root?): %s\n", strerror(res)); } } mon_is_ready = true; while (c->global->running) { fpgad_mutex_lock(err, &mon_list_lock); for (d = monitored_device_list ; d ; d = d->next) { mon_monitor(d); } fpgad_mutex_unlock(err, &mon_list_lock); usleep(c->global->poll_interval_usec); } while (evt_dispatcher_is_ready()) { // Wait for the event dispatcher to complete // before we destroy the monitored devices. usleep(c->global->poll_interval_usec); } mon_destroy(c->global); mon_is_ready = false; LOG("exiting\n"); return NULL; } void mon_monitor_device(fpgad_monitored_device *d) { int err; fpgad_monitored_device *trav; fpgad_mutex_lock(err, &mon_list_lock); d->next = NULL; if (!monitored_device_list) { monitored_device_list = d; goto out_unlock; } for (trav = monitored_device_list ; trav->next ; trav = trav->next) /* find the end of the list */ ; trav->next = d; out_unlock: fpgad_mutex_unlock(err, &mon_list_lock); } void mon_destroy(struct fpgad_config *c) { unsigned i; int err; fpgad_monitored_device *d; fpgad_mutex_lock(err, &mon_list_lock); for (d = monitored_device_list ; d ; ) { fpgad_monitored_device *trash = d; fpgad_plugin_destroy_t destroy; d = d->next; if (trash->type == FPGAD_PLUGIN_TYPE_THREAD) { if (trash->thread_stop_fn) { trash->thread_stop_fn(); } else { LOG("Thread plugin \"%s\" has" " no thread_stop_fn\n", trash->supported->library_path); pthread_cancel(trash->thread); } pthread_join(trash->thread, NULL); } destroy = (fpgad_plugin_destroy_t) dlsym(trash->supported->dl_handle, FPGAD_PLUGIN_DESTROY); if (destroy) { destroy(trash); } else { LOG("warning - no destructor for \"%s\"\n", trash->supported->library_path); } if (trash->token) fpgaDestroyToken(&trash->token); free(trash); } monitored_device_list = NULL; if (c->supported_devices) { for (i = 0 ; c->supported_devices[i].library_path ; ++i) { fpgad_supported_device *d = &c->supported_devices[i]; if (d->flags & FPGAD_DEV_LOADED) { dlclose(d->dl_handle); } d->flags = 0; d->dl_handle = NULL; } } fpgad_mutex_unlock(err, &mon_list_lock); }