/* * Copyright (c) 2014, Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * 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. * * 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 HOLDER 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. */ #include "config.h" #include #include #include #include "rdma/fabric.h" #include "rdma/fi_errno.h" #include "ofi.h" #include "usnic_direct.h" #include "usdf.h" #include "usdf_timer.h" enum { USDF_TF_QUEUED = (1 << 0), USDF_TF_ACTIVE = (1 << 1), USDF_TF_FREED = (1 << 2) }; #define USDF_NUM_TIMER_BUCKETS (16 * 1024) /* roughly 16 seconds max delay */ struct usdf_timer_entry { struct usdf_fabric *te_fabric; usdf_timer_callback_t te_callback; void *te_context; uint32_t te_flags; LIST_ENTRY(usdf_timer_entry) te_link; }; /* * Create a timer entry, registering a callback and argument. */ int usdf_timer_alloc(usdf_timer_callback_t cb, void *context, struct usdf_timer_entry **entry_o) { struct usdf_timer_entry *entry; entry = calloc(1, sizeof(*entry)); if (entry == NULL) { return -FI_ENOMEM; } entry->te_callback = cb; entry->te_context = context; entry->te_flags = 0; *entry_o = entry; return 0; } void usdf_timer_free(struct usdf_fabric *fp, struct usdf_timer_entry *entry) { pthread_spin_lock(&fp->fab_timer_lock); if (entry->te_flags & USDF_TF_ACTIVE) { entry->te_flags |= USDF_TF_FREED; } else { if (entry->te_flags & USDF_TF_QUEUED) { LIST_REMOVE(entry, te_link); } free(entry); } pthread_spin_unlock(&fp->fab_timer_lock); } void usdf_timer_cancel(struct usdf_fabric *fp, struct usdf_timer_entry *entry) { pthread_spin_lock(&fp->fab_timer_lock); if (entry->te_flags & USDF_TF_QUEUED) { LIST_REMOVE(entry, te_link); entry->te_flags &= ~USDF_TF_QUEUED; --fp->fab_active_timer_count; } pthread_spin_unlock(&fp->fab_timer_lock); } /* * Set this timer to fire "ms" milliseconds from now. If the timer is already * queued, previous timeout will be discarded. * * When timer expires, the registered timer callback will be called and * the timer entry removed from the queued list. The timer routine will not * be called again until usdf_timer_set() is called again to re-set it. * usdf_timer_set() is safe to call from timer service routine. */ static inline int _usdf_timer_do_set(struct usdf_fabric *fp, struct usdf_timer_entry *entry, uint32_t ms) { int ret; unsigned bucket; /* If no timers active, cur_bucket_ms may need catchup */ ++fp->fab_active_timer_count; if (fp->fab_active_timer_count == 1) { fp->fab_cur_bucket_ms = usdf_get_ms(); ret = usdf_fabric_wake_thread(fp); if (ret != 0) { --fp->fab_active_timer_count; return ret; } } if (entry->te_flags & USDF_TF_QUEUED) { LIST_REMOVE(entry, te_link); --fp->fab_active_timer_count; } // we could make "overflow" bucket... if (ms >= USDF_NUM_TIMER_BUCKETS) { --fp->fab_active_timer_count; return -FI_EINVAL; } bucket = (fp->fab_cur_bucket + ms) & (USDF_NUM_TIMER_BUCKETS - 1); LIST_INSERT_HEAD(&fp->fab_timer_buckets[bucket], entry, te_link); entry->te_flags |= USDF_TF_QUEUED; return 0; } int usdf_timer_set(struct usdf_fabric *fp, struct usdf_timer_entry *entry, uint32_t ms) { int ret; pthread_spin_lock(&fp->fab_timer_lock); if (entry->te_flags & USDF_TF_QUEUED) { ret = 0; } else { ret = _usdf_timer_do_set(fp, entry, ms); } pthread_spin_unlock(&fp->fab_timer_lock); return ret; } int usdf_timer_reset(struct usdf_fabric *fp, struct usdf_timer_entry *entry, uint32_t ms) { int ret; pthread_spin_lock(&fp->fab_timer_lock); ret = _usdf_timer_do_set(fp, entry, ms); pthread_spin_unlock(&fp->fab_timer_lock); return ret; } static inline void usdf_run_bucket(struct usdf_fabric *fp, struct usdf_timer_bucket *bp) { struct usdf_timer_entry *entry; while (!LIST_EMPTY(bp)) { entry = LIST_FIRST(bp); LIST_REMOVE(entry, te_link); entry->te_flags |= USDF_TF_ACTIVE; entry->te_flags &= ~USDF_TF_QUEUED; --fp->fab_active_timer_count; /* call timer service routine without lock */ pthread_spin_unlock(&fp->fab_timer_lock); entry->te_callback(entry->te_context); pthread_spin_lock(&fp->fab_timer_lock); } } /* * Called only from fabric progression thread */ void usdf_timer_progress(struct usdf_fabric *fp) { pthread_spin_lock(&fp->fab_timer_lock); while (fp->fab_cur_bucket_ms < usdf_get_ms()) { usdf_run_bucket(fp, &fp->fab_timer_buckets[fp->fab_cur_bucket]); ++fp->fab_cur_bucket_ms; fp->fab_cur_bucket = (fp->fab_cur_bucket + 1) & (USDF_NUM_TIMER_BUCKETS - 1); } pthread_spin_unlock(&fp->fab_timer_lock); } /* * Initialize timer data */ int usdf_timer_init(struct usdf_fabric *fp) { int i; pthread_spin_init(&fp->fab_timer_lock, PTHREAD_PROCESS_PRIVATE); fp->fab_timer_buckets = calloc(USDF_NUM_TIMER_BUCKETS, sizeof(struct usdf_timer_bucket)); if (fp->fab_timer_buckets == NULL) { return -FI_ENOMEM; } for (i = 0; i < USDF_NUM_TIMER_BUCKETS; ++i) { LIST_INIT(&fp->fab_timer_buckets[i]); } fp->fab_cur_bucket = 0; fp->fab_cur_bucket_ms = usdf_get_ms(); return 0; } void usdf_timer_deinit(struct usdf_fabric *fp) { free(fp->fab_timer_buckets); }