/*- * Copyright (c) 2017 Broadcom. All rights reserved. * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 3. Neither the name of the copyright holder 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 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. * * $FreeBSD$ */ /** * @file * */ #include "ocs.h" #include "ocs_os.h" #define DEFAULT_SLAB_LEN (64*1024) struct ocs_array_s { ocs_os_handle_t os; uint32_t size; uint32_t count; uint32_t n_rows; uint32_t elems_per_row; uint32_t bytes_per_row; void **array_rows; uint32_t array_rows_len; }; static uint32_t slab_len = DEFAULT_SLAB_LEN; /** * @brief Set array slab allocation length * * The slab length is the maximum allocation length that the array uses. * The default 64k slab length may be overridden using this function. * * @param len new slab length. * * @return none */ void ocs_array_set_slablen(uint32_t len) { slab_len = len; } /** * @brief Allocate an array object * * An array object of size and number of elements is allocated * * @param os OS handle * @param size size of array elements in bytes * @param count number of elements in array * * @return pointer to array object or NULL */ ocs_array_t * ocs_array_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count) { ocs_array_t *array = NULL; uint32_t i; /* Fail if the item size exceeds slab_len - caller should increase slab_size, * or not use this API. */ if (size > slab_len) { ocs_log_err(NULL, "Error: size exceeds slab length\n"); return NULL; } array = ocs_malloc(os, sizeof(*array), OCS_M_ZERO | OCS_M_NOWAIT); if (array == NULL) { return NULL; } array->os = os; array->size = size; array->count = count; array->elems_per_row = slab_len / size; array->n_rows = (count + array->elems_per_row - 1) / array->elems_per_row; array->bytes_per_row = array->elems_per_row * array->size; array->array_rows_len = array->n_rows * sizeof(*array->array_rows); array->array_rows = ocs_malloc(os, array->array_rows_len, OCS_M_ZERO | OCS_M_NOWAIT); if (array->array_rows == NULL) { ocs_array_free(array); return NULL; } for (i = 0; i < array->n_rows; i++) { array->array_rows[i] = ocs_malloc(os, array->bytes_per_row, OCS_M_ZERO | OCS_M_NOWAIT); if (array->array_rows[i] == NULL) { ocs_array_free(array); return NULL; } } return array; } /** * @brief Free an array object * * Frees a prevously allocated array object * * @param array pointer to array object * * @return none */ void ocs_array_free(ocs_array_t *array) { uint32_t i; if (array != NULL) { if (array->array_rows != NULL) { for (i = 0; i < array->n_rows; i++) { if (array->array_rows[i] != NULL) { ocs_free(array->os, array->array_rows[i], array->bytes_per_row); } } ocs_free(array->os, array->array_rows, array->array_rows_len); } ocs_free(array->os, array, sizeof(*array)); } } /** * @brief Return reference to an element of an array object * * Return the address of an array element given an index * * @param array pointer to array object * @param idx array element index * * @return rointer to array element, or NULL if index out of range */ void *ocs_array_get(ocs_array_t *array, uint32_t idx) { void *entry = NULL; if (idx < array->count) { uint32_t row = idx / array->elems_per_row; uint32_t offset = idx % array->elems_per_row; entry = ((uint8_t*)array->array_rows[row]) + (offset * array->size); } return entry; } /** * @brief Return number of elements in an array * * Return the number of elements in an array * * @param array pointer to array object * * @return returns count of elements in an array */ uint32_t ocs_array_get_count(ocs_array_t *array) { return array->count; } /** * @brief Return size of array elements in bytes * * Returns the size in bytes of each array element * * @param array pointer to array object * * @return size of array element */ uint32_t ocs_array_get_size(ocs_array_t *array) { return array->size; } /** * @brief Void pointer array structure * * This structure describes an object consisting of an array of void * pointers. The object is allocated with a maximum array size, entries * are then added to the array with while maintaining an entry count. A set of * iterator APIs are included to allow facilitate cycling through the array * entries in a circular fashion. * */ struct ocs_varray_s { ocs_os_handle_t os; uint32_t array_count; /*>> maximum entry count in array */ void **array; /*>> pointer to allocated array memory */ uint32_t entry_count; /*>> number of entries added to the array */ uint32_t next_index; /*>> iterator next index */ ocs_lock_t lock; /*>> iterator lock */ }; /** * @brief Allocate a void pointer array * * A void pointer array of given length is allocated. * * @param os OS handle * @param array_count Array size * * @return returns a pointer to the ocs_varray_t object, other NULL on error */ ocs_varray_t * ocs_varray_alloc(ocs_os_handle_t os, uint32_t array_count) { ocs_varray_t *va; va = ocs_malloc(os, sizeof(*va), OCS_M_ZERO | OCS_M_NOWAIT); if (va != NULL) { va->os = os; va->array_count = array_count; va->array = ocs_malloc(os, sizeof(*va->array) * va->array_count, OCS_M_ZERO | OCS_M_NOWAIT); if (va->array != NULL) { va->next_index = 0; ocs_lock_init(os, &va->lock, "varray:%p", va); } else { ocs_free(os, va, sizeof(*va)); va = NULL; } } return va; } /** * @brief Free a void pointer array * * The void pointer array object is free'd * * @param va Pointer to void pointer array * * @return none */ void ocs_varray_free(ocs_varray_t *va) { if (va != NULL) { ocs_lock_free(&va->lock); if (va->array != NULL) { ocs_free(va->os, va->array, sizeof(*va->array) * va->array_count); } ocs_free(va->os, va, sizeof(*va)); } } /** * @brief Add an entry to a void pointer array * * An entry is added to the void pointer array * * @param va Pointer to void pointer array * @param entry Pointer to entry to add * * @return returns 0 if entry was added, -1 if there is no more space in the array */ int32_t ocs_varray_add(ocs_varray_t *va, void *entry) { uint32_t rc = -1; ocs_lock(&va->lock); if (va->entry_count < va->array_count) { va->array[va->entry_count++] = entry; rc = 0; } ocs_unlock(&va->lock); return rc; } /** * @brief Reset the void pointer array iterator * * The next index value of the void pointer array iterator is cleared. * * @param va Pointer to void pointer array * * @return none */ void ocs_varray_iter_reset(ocs_varray_t *va) { ocs_lock(&va->lock); va->next_index = 0; ocs_unlock(&va->lock); } /** * @brief Return next entry from a void pointer array * * The next entry in the void pointer array is returned. * * @param va Pointer to void point array * * Note: takes the void pointer array lock * * @return returns next void pointer entry */ void * ocs_varray_iter_next(ocs_varray_t *va) { void *rval = NULL; if (va != NULL) { ocs_lock(&va->lock); rval = _ocs_varray_iter_next(va); ocs_unlock(&va->lock); } return rval; } /** * @brief Return next entry from a void pointer array * * The next entry in the void pointer array is returned. * * @param va Pointer to void point array * * Note: doesn't take the void pointer array lock * * @return returns next void pointer entry */ void * _ocs_varray_iter_next(ocs_varray_t *va) { void *rval; rval = va->array[va->next_index]; if (++va->next_index >= va->entry_count) { va->next_index = 0; } return rval; } /** * @brief Take void pointer array lock * * Takes the lock for the given void pointer array * * @param va Pointer to void pointer array * * @return none */ void ocs_varray_lock(ocs_varray_t *va) { ocs_lock(&va->lock); } /** * @brief Release void pointer array lock * * Releases the lock for the given void pointer array * * @param va Pointer to void pointer array * * @return none */ void ocs_varray_unlock(ocs_varray_t *va) { ocs_unlock(&va->lock); } /** * @brief Return entry count for a void pointer array * * The entry count for a void pointer array is returned * * @param va Pointer to void pointer array * * @return returns entry count */ uint32_t ocs_varray_get_count(ocs_varray_t *va) { uint32_t rc; ocs_lock(&va->lock); rc = va->entry_count; ocs_unlock(&va->lock); return rc; } struct ocs_cbuf_s { ocs_os_handle_t os; /*<< OS handle */ uint32_t entry_count; /*<< entry count */ void **array; /*<< pointer to array of cbuf pointers */ uint32_t pidx; /*<< producer index */ uint32_t cidx; /*<< consumer index */ ocs_lock_t cbuf_plock; /*<< idx lock */ ocs_lock_t cbuf_clock; /*<< idx lock */ ocs_sem_t cbuf_psem; /*<< cbuf producer counting semaphore */ ocs_sem_t cbuf_csem; /*<< cbuf consumer counting semaphore */ }; /** * @brief Initialize a circular buffer queue * * A circular buffer with producer/consumer API is allocated * * @param os OS handle * @param entry_count count of entries * * @return returns pointer to circular buffer, or NULL */ ocs_cbuf_t* ocs_cbuf_alloc(ocs_os_handle_t os, uint32_t entry_count) { ocs_cbuf_t *cbuf; cbuf = ocs_malloc(os, sizeof(*cbuf), OCS_M_NOWAIT | OCS_M_ZERO); if (cbuf == NULL) { return NULL; } cbuf->os = os; cbuf->entry_count = entry_count; cbuf->pidx = 0; cbuf->cidx = 0; ocs_lock_init(NULL, &cbuf->cbuf_clock, "cbuf_c:%p", cbuf); ocs_lock_init(NULL, &cbuf->cbuf_plock, "cbuf_p:%p", cbuf); ocs_sem_init(&cbuf->cbuf_csem, 0, "cbuf:%p", cbuf); ocs_sem_init(&cbuf->cbuf_psem, cbuf->entry_count, "cbuf:%p", cbuf); cbuf->array = ocs_malloc(os, entry_count * sizeof(*cbuf->array), OCS_M_NOWAIT | OCS_M_ZERO); if (cbuf->array == NULL) { ocs_cbuf_free(cbuf); return NULL; } return cbuf; } /** * @brief Free a circular buffer * * The memory resources of a circular buffer are free'd * * @param cbuf pointer to circular buffer * * @return none */ void ocs_cbuf_free(ocs_cbuf_t *cbuf) { if (cbuf != NULL) { if (cbuf->array != NULL) { ocs_free(cbuf->os, cbuf->array, sizeof(*cbuf->array) * cbuf->entry_count); } ocs_lock_free(&cbuf->cbuf_clock); ocs_lock_free(&cbuf->cbuf_plock); ocs_free(cbuf->os, cbuf, sizeof(*cbuf)); } } /** * @brief Get pointer to buffer * * Wait for a buffer to become available, and return a pointer to the buffer. * * @param cbuf pointer to circular buffer * @param timeout_usec timeout in microseconds * * @return pointer to buffer, or NULL if timeout */ void* ocs_cbuf_get(ocs_cbuf_t *cbuf, int32_t timeout_usec) { void *ret = NULL; if (likely(ocs_sem_p(&cbuf->cbuf_csem, timeout_usec) == 0)) { ocs_lock(&cbuf->cbuf_clock); ret = cbuf->array[cbuf->cidx]; if (unlikely(++cbuf->cidx >= cbuf->entry_count)) { cbuf->cidx = 0; } ocs_unlock(&cbuf->cbuf_clock); ocs_sem_v(&cbuf->cbuf_psem); } return ret; } /** * @brief write a buffer * * The buffer is written to the circular buffer. * * @param cbuf pointer to circular buffer * @param elem pointer to entry * * @return returns 0 for success, a negative error code value for failure. */ int32_t ocs_cbuf_put(ocs_cbuf_t *cbuf, void *elem) { int32_t rc = 0; if (likely(ocs_sem_p(&cbuf->cbuf_psem, -1) == 0)) { ocs_lock(&cbuf->cbuf_plock); cbuf->array[cbuf->pidx] = elem; if (unlikely(++cbuf->pidx >= cbuf->entry_count)) { cbuf->pidx = 0; } ocs_unlock(&cbuf->cbuf_plock); ocs_sem_v(&cbuf->cbuf_csem); } else { rc = -1; } return rc; } /** * @brief Prime a circular buffer data * * Post array buffers to a circular buffer * * @param cbuf pointer to circular buffer * @param array pointer to buffer array * * @return returns 0 for success, a negative error code value for failure. */ int32_t ocs_cbuf_prime(ocs_cbuf_t *cbuf, ocs_array_t *array) { uint32_t i; uint32_t count = MIN(ocs_array_get_count(array), cbuf->entry_count); for (i = 0; i < count; i++) { ocs_cbuf_put(cbuf, ocs_array_get(array, i)); } return 0; } /** * @brief Generate driver dump start of file information * * The start of file information is added to 'textbuf' * * @param textbuf pointer to driver dump text buffer * * @return none */ void ocs_ddump_startfile(ocs_textbuf_t *textbuf) { ocs_textbuf_printf(textbuf, "\n"); } /** * @brief Generate driver dump end of file information * * The end of file information is added to 'textbuf' * * @param textbuf pointer to driver dump text buffer * * @return none */ void ocs_ddump_endfile(ocs_textbuf_t *textbuf) { } /** * @brief Generate driver dump section start data * * The driver section start information is added to textbuf * * @param textbuf pointer to text buffer * @param name name of section * @param instance instance number of this section * * @return none */ void ocs_ddump_section(ocs_textbuf_t *textbuf, const char *name, uint32_t instance) { ocs_textbuf_printf(textbuf, "<%s type=\"section\" instance=\"%d\">\n", name, instance); } /** * @brief Generate driver dump section end data * * The driver section end information is added to textbuf * * @param textbuf pointer to text buffer * @param name name of section * @param instance instance number of this section * * @return none */ void ocs_ddump_endsection(ocs_textbuf_t *textbuf, const char *name, uint32_t instance) { ocs_textbuf_printf(textbuf, "\n", name); } /** * @brief Generate driver dump data for a given value * * A value is added to textbuf * * @param textbuf pointer to text buffer * @param name name of variable * @param fmt snprintf format specifier * * @return none */ void ocs_ddump_value(ocs_textbuf_t *textbuf, const char *name, const char *fmt, ...) { va_list ap; char valuebuf[64]; va_start(ap, fmt); vsnprintf(valuebuf, sizeof(valuebuf), fmt, ap); va_end(ap); ocs_textbuf_printf(textbuf, "<%s>%s\n", name, valuebuf, name); } /** * @brief Generate driver dump data for an arbitrary buffer of DWORDS * * A status value is added to textbuf * * @param textbuf pointer to text buffer * @param name name of status variable * @param instance instance number of this section * @param buffer buffer to print * @param size size of buffer in bytes * * @return none */ void ocs_ddump_buffer(ocs_textbuf_t *textbuf, const char *name, uint32_t instance, void *buffer, uint32_t size) { uint32_t *dword; uint32_t i; uint32_t count; count = size / sizeof(uint32_t); if (count == 0) { return; } ocs_textbuf_printf(textbuf, "<%s type=\"buffer\" instance=\"%d\">\n", name, instance); dword = buffer; for (i = 0; i < count; i++) { #define OCS_NEWLINE_MOD 8 ocs_textbuf_printf(textbuf, "%08x ", *dword++); if ((i % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1)) { ocs_textbuf_printf(textbuf, "\n"); } } ocs_textbuf_printf(textbuf, "\n", name); } /** * @brief Generate driver dump for queue * * Add queue elements to text buffer * * @param textbuf pointer to driver dump text buffer * @param q_addr address of start of queue * @param size size of each queue entry * @param length number of queue entries in the queue * @param index current index of queue * @param qentries number of most recent queue entries to dump * * @return none */ void ocs_ddump_queue_entries(ocs_textbuf_t *textbuf, void *q_addr, uint32_t size, uint32_t length, int32_t index, uint32_t qentries) { uint32_t i; uint32_t j; uint8_t *entry; uint32_t *dword; uint32_t entry_count = 0; uint32_t entry_words = size / sizeof(uint32_t); if ((qentries == (uint32_t)-1) || (qentries > length)) { /* if qentries is -1 or larger than queue size, dump entire queue */ entry_count = length; index = 0; } else { entry_count = qentries; index -= (qentries - 1); if (index < 0) { index += length; } } #define OCS_NEWLINE_MOD 8 ocs_textbuf_printf(textbuf, "\n"); for (i = 0; i < entry_count; i++){ entry = q_addr; entry += index * size; dword = (uint32_t *)entry; ocs_textbuf_printf(textbuf, "[%04x] ", index); for (j = 0; j < entry_words; j++) { ocs_textbuf_printf(textbuf, "%08x ", *dword++); if (((j+1) == entry_words) || ((j % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1))) { ocs_textbuf_printf(textbuf, "\n"); if ((j+1) < entry_words) { ocs_textbuf_printf(textbuf, " "); } } } index++; if ((uint32_t)index >= length) { index = 0; } } ocs_textbuf_printf(textbuf, "\n"); } #define OCS_DEBUG_ENABLE(x) (x ? ~0 : 0) #define OCS_DEBUG_MASK \ (OCS_DEBUG_ENABLE(1) & OCS_DEBUG_ALWAYS) | \ (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_MQ_DUMP) | \ (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_CQ_DUMP) | \ (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_WQ_DUMP) | \ (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_EQ_DUMP) | \ (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_SPARAM_DUMP) static uint32_t ocs_debug_mask = OCS_DEBUG_MASK; static int _isprint(int c) { return ((c > 32) && (c < 127)); } /** * @ingroup debug * @brief enable debug options * * Enables debug options by or-ing in mask into the currently enabled * debug mask. * * @param mask mask bits to enable * * @return none */ void ocs_debug_enable(uint32_t mask) { ocs_debug_mask |= mask; } /** * @ingroup debug * @brief disable debug options * * Disables debug options by clearing bits in mask into the currently enabled * debug mask. * * @param mask mask bits to enable * * @return none */ void ocs_debug_disable(uint32_t mask) { ocs_debug_mask &= ~mask; } /** * @ingroup debug * @brief return true if debug bits are enabled * * Returns true if the request debug bits are set. * * @param mask debug bit mask * * @return true if corresponding bits are set * * @note Passing in a mask value of zero always returns true */ int ocs_debug_is_enabled(uint32_t mask) { return (ocs_debug_mask & mask) == mask; } /** * @ingroup debug * @brief Dump 32 bit hex/ascii data * * Dumps using ocs_log a buffer of data as 32 bit hex and ascii * * @param mask debug enable bits * @param os os handle * @param label text label for the display (may be NULL) * @param buf pointer to data buffer * @param buf_length length of data buffer * * @return none * */ void ocs_dump32(uint32_t mask, ocs_os_handle_t os, const char *label, void *buf, uint32_t buf_length) { uint32_t word_count = buf_length / sizeof(uint32_t); uint32_t i; uint32_t columns = 8; uint32_t n; uint32_t *wbuf; char *cbuf; uint32_t addr = 0; char linebuf[200]; char *pbuf = linebuf; if (!ocs_debug_is_enabled(mask)) return; if (label) ocs_log_debug(os, "%s\n", label); wbuf = buf; while (word_count > 0) { pbuf = linebuf; pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X: ", addr); n = word_count; if (n > columns) n = columns; for (i = 0; i < n; i ++) pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X ", wbuf[i]); for (; i < columns; i ++) pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%8s ", ""); pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), " "); cbuf = (char*)wbuf; for (i = 0; i < n*sizeof(uint32_t); i ++) pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%c", _isprint(cbuf[i]) ? cbuf[i] : '.'); pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "\n"); ocs_log_debug(os, "%s", linebuf); wbuf += n; word_count -= n; addr += n*sizeof(uint32_t); } } #if defined(OCS_DEBUG_QUEUE_HISTORY) /* each bit corresponds to word to capture */ #define OCS_Q_HIST_WQE_WORD_MASK_DEFAULT (BIT(4) | BIT(6) | BIT(7) | BIT(9) | BIT(12)) #define OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(12)) #define OCS_Q_HIST_IWRITE_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9)) #define OCS_Q_HIST_IREAD_WQE_WORD_MASK (BIT(4) | BIT(6) | BIT(7) | BIT(9)) #define OCS_Q_HIST_ABORT_WQE_WORD_MASK (BIT(3) | BIT(7) | BIT(8) | BIT(9)) #define OCS_Q_HIST_WCQE_WORD_MASK (BIT(0) | BIT(3)) #define OCS_Q_HIST_WCQE_WORD_MASK_ERR (BIT(0) | BIT(1) | BIT(2) | BIT(3)) #define OCS_Q_HIST_CQXABT_WORD_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) /* if set, will provide extra queue information in each entry */ #define OCS_Q_HIST_ENABLE_Q_INFO 0 uint8_t ocs_queue_history_q_info_enabled(void) { return OCS_Q_HIST_ENABLE_Q_INFO; } /* if set, will provide timestamps in each entry */ #define OCS_Q_HIST_ENABLE_TIMESTAMPS 0 uint8_t ocs_queue_history_timestamp_enabled(void) { return OCS_Q_HIST_ENABLE_TIMESTAMPS; } /* Add WQEs and masks to override default WQE mask */ ocs_q_hist_wqe_mask_t ocs_q_hist_wqe_masks[] = { /* WQE command Word mask */ {SLI4_WQE_ABORT, OCS_Q_HIST_ABORT_WQE_WORD_MASK}, {SLI4_WQE_FCP_IREAD64, OCS_Q_HIST_IREAD_WQE_WORD_MASK}, {SLI4_WQE_FCP_IWRITE64, OCS_Q_HIST_IWRITE_WQE_WORD_MASK}, {SLI4_WQE_FCP_CONT_TRECEIVE64, OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK}, }; /* CQE masks */ ocs_q_hist_cqe_mask_t ocs_q_hist_cqe_masks[] = { /* CQE type Q_hist_type mask (success) mask (non-success) */ {SLI_QENTRY_WQ, OCS_Q_HIST_TYPE_CWQE, OCS_Q_HIST_WCQE_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK_ERR}, {SLI_QENTRY_XABT, OCS_Q_HIST_TYPE_CXABT, OCS_Q_HIST_CQXABT_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK}, }; static uint32_t ocs_q_hist_get_wqe_mask(sli4_generic_wqe_t *wqe) { uint32_t i; for (i = 0; i < ARRAY_SIZE(ocs_q_hist_wqe_masks); i++) { if (ocs_q_hist_wqe_masks[i].command == wqe->command) { return ocs_q_hist_wqe_masks[i].mask; } } /* return default WQE mask */ return OCS_Q_HIST_WQE_WORD_MASK_DEFAULT; } /** * @ingroup debug * @brief Initialize resources for queue history * * @param os os handle * @param q_hist Pointer to the queue history object. * * @return none */ void ocs_queue_history_init(ocs_t *ocs, ocs_hw_q_hist_t *q_hist) { q_hist->ocs = ocs; if (q_hist->q_hist != NULL) { /* Setup is already done */ ocs_log_debug(ocs, "q_hist not NULL, skipping init\n"); return; } q_hist->q_hist = ocs_malloc(ocs, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE, OCS_M_ZERO | OCS_M_NOWAIT); if (q_hist->q_hist == NULL) { ocs_log_err(ocs, "Could not allocate queue history buffer\n"); } else { ocs_lock_init(ocs, &q_hist->q_hist_lock, "queue history lock[%d]", ocs_instance(ocs)); } q_hist->q_hist_index = 0; } /** * @ingroup debug * @brief Free resources for queue history * * @param q_hist Pointer to the queue history object. * * @return none */ void ocs_queue_history_free(ocs_hw_q_hist_t *q_hist) { ocs_t *ocs = q_hist->ocs; if (q_hist->q_hist != NULL) { ocs_free(ocs, q_hist->q_hist, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE); ocs_lock_free(&q_hist->q_hist_lock); q_hist->q_hist = NULL; } } static void ocs_queue_history_add_q_info(ocs_hw_q_hist_t *q_hist, uint32_t qid, uint32_t qindex) { if (ocs_queue_history_q_info_enabled()) { /* write qid, index */ q_hist->q_hist[q_hist->q_hist_index] = (qid << 16) | qindex; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } } static void ocs_queue_history_add_timestamp(ocs_hw_q_hist_t *q_hist) { if (ocs_queue_history_timestamp_enabled()) { /* write tsc */ uint64_t tsc_value; tsc_value = get_cyclecount(); q_hist->q_hist[q_hist->q_hist_index] = ((tsc_value >> 32 ) & 0xFFFFFFFF); q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; q_hist->q_hist[q_hist->q_hist_index] = (tsc_value & 0xFFFFFFFF); q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } } /** * @ingroup debug * @brief Log work queue entry (WQE) into history array * * @param q_hist Pointer to the queue history object. * @param entryw Work queue entry in words * @param qid Queue ID * @param qindex Queue index * * @return none */ void ocs_queue_history_wq(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t qid, uint32_t qindex) { int i; ocs_q_hist_ftr_t ftr; uint32_t wqe_word_mask = ocs_q_hist_get_wqe_mask((sli4_generic_wqe_t *)entryw); if (q_hist->q_hist == NULL) { /* Can't save anything */ return; } ftr.word = 0; ftr.s.type = OCS_Q_HIST_TYPE_WQE; ocs_lock(&q_hist->q_hist_lock); /* Capture words in reverse order since we'll be interpretting them LIFO */ for (i = ((sizeof(wqe_word_mask)*8) - 1); i >= 0; i--){ if ((wqe_word_mask >> i) & 1) { q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } } ocs_queue_history_add_q_info(q_hist, qid, qindex); ocs_queue_history_add_timestamp(q_hist); /* write footer */ if (wqe_word_mask) { ftr.s.mask = wqe_word_mask; q_hist->q_hist[q_hist->q_hist_index] = ftr.word; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } ocs_unlock(&q_hist->q_hist_lock); } /** * @ingroup debug * @brief Log misc words * * @param q_hist Pointer to the queue history object. * @param entryw array of words * @param num_words number of words in entryw * * @return none */ void ocs_queue_history_misc(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t num_words) { int i; ocs_q_hist_ftr_t ftr; uint32_t mask = 0; if (q_hist->q_hist == NULL) { /* Can't save anything */ return; } ftr.word = 0; ftr.s.type = OCS_Q_HIST_TYPE_MISC; ocs_lock(&q_hist->q_hist_lock); /* Capture words in reverse order since we'll be interpretting them LIFO */ for (i = num_words-1; i >= 0; i--) { q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; mask |= BIT(i); } ocs_queue_history_add_timestamp(q_hist); /* write footer */ if (num_words) { ftr.s.mask = mask; q_hist->q_hist[q_hist->q_hist_index] = ftr.word; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } ocs_unlock(&q_hist->q_hist_lock); } /** * @ingroup debug * @brief Log work queue completion (CQE) entry into history * array * * @param q_hist Pointer to the queue history object. * @param ctype Type of completion entry * @param entryw Completion queue entry in words * @param status Completion queue status * @param qid Queue ID * @param qindex Queue index * * @return none */ void ocs_queue_history_cqe(ocs_hw_q_hist_t *q_hist, uint8_t ctype, uint32_t *entryw, uint8_t status, uint32_t qid, uint32_t qindex) { int i; unsigned j; uint32_t cqe_word_mask = 0; ocs_q_hist_ftr_t ftr; if (q_hist->q_hist == NULL) { /* Can't save anything */ return; } ftr.word = 0; for (j = 0; j < ARRAY_SIZE(ocs_q_hist_cqe_masks); j++) { if (ocs_q_hist_cqe_masks[j].ctype == ctype) { ftr.s.type = ocs_q_hist_cqe_masks[j].type; if (status != 0) { cqe_word_mask = ocs_q_hist_cqe_masks[j].mask_err; } else { cqe_word_mask = ocs_q_hist_cqe_masks[j].mask; } } } ocs_lock(&q_hist->q_hist_lock); /* Capture words in reverse order since we'll be interpretting them LIFO */ for (i = ((sizeof(cqe_word_mask)*8) - 1); i >= 0; i--){ if ((cqe_word_mask >> i) & 1) { q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } } ocs_queue_history_add_q_info(q_hist, qid, qindex); ocs_queue_history_add_timestamp(q_hist); /* write footer */ if (cqe_word_mask) { ftr.s.mask = cqe_word_mask; q_hist->q_hist[q_hist->q_hist_index] = ftr.word; q_hist->q_hist_index++; q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; } ocs_unlock(&q_hist->q_hist_lock); } /** * @brief Get previous index * * @param index Index from which previous index is derived. */ uint32_t ocs_queue_history_prev_index(uint32_t index) { if (index == 0) { return OCS_Q_HIST_SIZE - 1; } else { return index - 1; } } #endif /* OCS_DEBUG_QUEUE_HISTORY */ /** * @brief Display service parameters * * * * @param prelabel leading display label * @param reqlabel display label * @param dest destination 0=ocs_log, 1=textbuf * @param textbuf text buffer destination (if dest==1) * @param sparams pointer to service parameter * * @return none */ void ocs_display_sparams(const char *prelabel, const char *reqlabel, int dest, void *textbuf, void *sparams) { char label[64]; if (sparams == NULL) { return; } switch(dest) { case 0: if (prelabel != NULL) { ocs_snprintf(label, sizeof(label), "[%s] sparam: %s", prelabel, reqlabel); } else { ocs_snprintf(label, sizeof(label), "sparam: %s", reqlabel); } ocs_dump32(OCS_DEBUG_ENABLE_SPARAM_DUMP, NULL, label, sparams, sizeof(fc_plogi_payload_t)); break; case 1: ocs_ddump_buffer((ocs_textbuf_t*) textbuf, reqlabel, 0, sparams, sizeof(fc_plogi_payload_t)); break; } } /** * @brief Calculate the T10 PI CRC guard value for a block. * * @param buffer Pointer to the data buffer. * @param size Number of bytes. * @param crc Previously-calculated CRC, or 0 for a new block. * * @return Returns the calculated CRC, which may be passed back in for partial blocks. * */ uint16_t ocs_scsi_dif_calc_crc(const uint8_t *buffer, uint32_t size, uint16_t crc) { return t10crc16(buffer, size, crc); } /** * @brief Calculate the IP-checksum guard value for a block. * * @param addrlen array of address length pairs * @param addrlen_count number of entries in the addrlen[] array * * Algorithm: * Sum all all the 16-byte words in the block * Add in the "carry", which is everything in excess of 16-bits * Flip all the bits * * @return Returns the calculated checksum */ uint16_t ocs_scsi_dif_calc_checksum(ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count) { uint32_t i, j; uint16_t checksum; uint32_t intermediate; /* Use an intermediate to hold more than 16 bits during calculations */ uint32_t count; uint16_t *buffer; intermediate = 0; for (j = 0; j < addrlen_count; j++) { buffer = addrlen[j].vaddr; count = addrlen[j].length / 2; for (i=0; i < count; i++) { intermediate += buffer[i]; } } /* Carry is everything over 16 bits */ intermediate += ((intermediate & 0xffff0000) >> 16); /* Flip all the bits */ intermediate = ~intermediate; checksum = intermediate; return checksum; } /** * @brief Return blocksize given SCSI API DIF block size * * Given the DIF block size enumerated value, return the block size value. (e.g. * OCS_SCSI_DIF_BLK_SIZE_512 returns 512) * * @param dif_info Pointer to SCSI API DIF info block * * @return returns block size, or 0 if SCSI API DIF blocksize is invalid */ uint32_t ocs_scsi_dif_blocksize(ocs_scsi_dif_info_t *dif_info) { uint32_t blocksize = 0; switch(dif_info->blk_size) { case OCS_SCSI_DIF_BK_SIZE_512: blocksize = 512; break; case OCS_SCSI_DIF_BK_SIZE_1024: blocksize = 1024; break; case OCS_SCSI_DIF_BK_SIZE_2048: blocksize = 2048; break; case OCS_SCSI_DIF_BK_SIZE_4096: blocksize = 4096; break; case OCS_SCSI_DIF_BK_SIZE_520: blocksize = 520; break; case OCS_SCSI_DIF_BK_SIZE_4104: blocksize = 4104; break; default: break; } return blocksize; } /** * @brief Set SCSI API DIF blocksize * * Given a blocksize value (512, 1024, etc.), set the SCSI API DIF blocksize * in the DIF info block * * @param dif_info Pointer to the SCSI API DIF info block * @param blocksize Block size * * @return returns 0 for success, a negative error code value for failure. */ int32_t ocs_scsi_dif_set_blocksize(ocs_scsi_dif_info_t *dif_info, uint32_t blocksize) { int32_t rc = 0; switch(blocksize) { case 512: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_512; break; case 1024: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_1024; break; case 2048: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_2048; break; case 4096: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4096; break; case 520: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_520; break; case 4104: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4104; break; default: rc = -1; break; } return rc; } /** * @brief Return memory block size given SCSI DIF API * * The blocksize in memory for the DIF transfer is returned, given the SCSI DIF info * block and the direction of transfer. * * @param dif_info Pointer to DIF info block * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire * * @return Memory blocksize, or negative error value * * WARNING: the order of initialization of the adj[] arrays MUST match the declarations * of OCS_SCSI_DIF_OPER_* */ int32_t ocs_scsi_dif_mem_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem) { uint32_t blocksize; uint8_t wiretomem_adj[] = { 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ uint8_t memtowire_adj[] = { 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ blocksize = ocs_scsi_dif_blocksize(dif_info); if (blocksize == 0) { return -1; } if (wiretomem) { ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); blocksize += wiretomem_adj[dif_info->dif_oper]; } else { /* mem to wire */ ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); blocksize += memtowire_adj[dif_info->dif_oper]; } return blocksize; } /** * @brief Return wire block size given SCSI DIF API * * The blocksize on the wire for the DIF transfer is returned, given the SCSI DIF info * block and the direction of transfer. * * @param dif_info Pointer to DIF info block * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire * * @return Wire blocksize or negative error value * * WARNING: the order of initialization of the adj[] arrays MUST match the declarations * of OCS_SCSI_DIF_OPER_* */ int32_t ocs_scsi_dif_wire_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem) { uint32_t blocksize; uint8_t wiretomem_adj[] = { 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ uint8_t memtowire_adj[] = { 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ blocksize = ocs_scsi_dif_blocksize(dif_info); if (blocksize == 0) { return -1; } if (wiretomem) { ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); blocksize += wiretomem_adj[dif_info->dif_oper]; } else { /* mem to wire */ ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); blocksize += memtowire_adj[dif_info->dif_oper]; } return blocksize; } /** * @brief Return blocksize given HW API DIF block size * * Given the DIF block size enumerated value, return the block size value. (e.g. * OCS_SCSI_DIF_BLK_SIZE_512 returns 512) * * @param dif_info Pointer to HW API DIF info block * * @return returns block size, or 0 if HW API DIF blocksize is invalid */ uint32_t ocs_hw_dif_blocksize(ocs_hw_dif_info_t *dif_info) { uint32_t blocksize = 0; switch(dif_info->blk_size) { case OCS_HW_DIF_BK_SIZE_512: blocksize = 512; break; case OCS_HW_DIF_BK_SIZE_1024: blocksize = 1024; break; case OCS_HW_DIF_BK_SIZE_2048: blocksize = 2048; break; case OCS_HW_DIF_BK_SIZE_4096: blocksize = 4096; break; case OCS_HW_DIF_BK_SIZE_520: blocksize = 520; break; case OCS_HW_DIF_BK_SIZE_4104: blocksize = 4104; break; default: break; } return blocksize; } /** * @brief Return memory block size given HW DIF API * * The blocksize in memory for the DIF transfer is returned, given the HW DIF info * block and the direction of transfer. * * @param dif_info Pointer to DIF info block * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire * * @return Memory blocksize, or negative error value * * WARNING: the order of initialization of the adj[] arrays MUST match the declarations * of OCS_HW_DIF_OPER_* */ int32_t ocs_hw_dif_mem_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem) { uint32_t blocksize; uint8_t wiretomem_adj[] = { 0, /* OCS_HW_DIF_OPER_DISABLED, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ uint8_t memtowire_adj[] = { 0, /* OCS_HW_DIF_OPER_DISABLED, */ 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ blocksize = ocs_hw_dif_blocksize(dif_info); if (blocksize == 0) { return -1; } if (wiretomem) { ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); blocksize += wiretomem_adj[dif_info->dif_oper]; } else { /* mem to wire */ ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); blocksize += memtowire_adj[dif_info->dif_oper]; } return blocksize; } /** * @brief Return wire block size given HW DIF API * * The blocksize on the wire for the DIF transfer is returned, given the HW DIF info * block and the direction of transfer. * * @param dif_info Pointer to DIF info block * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire * * @return Wire blocksize or negative error value * * WARNING: the order of initialization of the adj[] arrays MUST match the declarations * of OCS_HW_DIF_OPER_* */ int32_t ocs_hw_dif_wire_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem) { uint32_t blocksize; uint8_t wiretomem_adj[] = { 0, /* OCS_HW_DIF_OPER_DISABLED, */ 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ uint8_t memtowire_adj[] = { 0, /* OCS_HW_DIF_OPER_DISABLED, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ blocksize = ocs_hw_dif_blocksize(dif_info); if (blocksize == 0) { return -1; } if (wiretomem) { ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); blocksize += wiretomem_adj[dif_info->dif_oper]; } else { /* mem to wire */ ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); blocksize += memtowire_adj[dif_info->dif_oper]; } return blocksize; } static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment); static ocs_textbuf_segment_t *ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf); static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment); static ocs_textbuf_segment_t *ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx); uint8_t * ocs_textbuf_get_buffer(ocs_textbuf_t *textbuf) { return ocs_textbuf_ext_get_buffer(textbuf, 0); } int32_t ocs_textbuf_get_length(ocs_textbuf_t *textbuf) { return ocs_textbuf_ext_get_length(textbuf, 0); } int32_t ocs_textbuf_get_written(ocs_textbuf_t *textbuf) { uint32_t idx; int32_t n; int32_t total = 0; for (idx = 0; (n = ocs_textbuf_ext_get_written(textbuf, idx)) >= 0; idx++) { total += n; } return total; } uint8_t *ocs_textbuf_ext_get_buffer(ocs_textbuf_t *textbuf, uint32_t idx) { ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); if (segment == NULL) { return NULL; } return segment->buffer; } int32_t ocs_textbuf_ext_get_length(ocs_textbuf_t *textbuf, uint32_t idx) { ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); if (segment == NULL) { return -1; } return segment->buffer_length; } int32_t ocs_textbuf_ext_get_written(ocs_textbuf_t *textbuf, uint32_t idx) { ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); if (segment == NULL) { return -1; } return segment->buffer_written; } uint32_t ocs_textbuf_initialized(ocs_textbuf_t *textbuf) { return (textbuf->ocs != NULL); } int32_t ocs_textbuf_alloc(ocs_t *ocs, ocs_textbuf_t *textbuf, uint32_t length) { ocs_memset(textbuf, 0, sizeof(*textbuf)); textbuf->ocs = ocs; ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link); if (length > OCS_TEXTBUF_MAX_ALLOC_LEN) { textbuf->allocation_length = OCS_TEXTBUF_MAX_ALLOC_LEN; } else { textbuf->allocation_length = length; } /* mark as extendable */ textbuf->extendable = TRUE; /* save maximum allocation length */ textbuf->max_allocation_length = length; /* Add first segment */ return (ocs_textbuf_segment_alloc(textbuf) == NULL) ? -1 : 0; } static ocs_textbuf_segment_t * ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf) { ocs_textbuf_segment_t *segment = NULL; if (textbuf->extendable) { segment = ocs_malloc(textbuf->ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT); if (segment != NULL) { segment->buffer = ocs_malloc(textbuf->ocs, textbuf->allocation_length, OCS_M_ZERO | OCS_M_NOWAIT); if (segment->buffer != NULL) { segment->buffer_length = textbuf->allocation_length; segment->buffer_written = 0; ocs_list_add_tail(&textbuf->segment_list, segment); textbuf->total_allocation_length += textbuf->allocation_length; /* If we've allocated our limit, then mark as not extendable */ if (textbuf->total_allocation_length >= textbuf->max_allocation_length) { textbuf->extendable = 0; } } else { ocs_textbuf_segment_free(textbuf->ocs, segment); segment = NULL; } } } return segment; } static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment) { if (segment) { if (segment->buffer && !segment->user_allocated) { ocs_free(ocs, segment->buffer, segment->buffer_length); } ocs_free(ocs, segment, sizeof(*segment)); } } static ocs_textbuf_segment_t * ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx) { uint32_t i; ocs_textbuf_segment_t *segment; if (ocs_textbuf_initialized(textbuf)) { i = 0; ocs_list_foreach(&textbuf->segment_list, segment) { if (i == idx) { return segment; } i++; } } return NULL; } int32_t ocs_textbuf_init(ocs_t *ocs, ocs_textbuf_t *textbuf, void *buffer, uint32_t length) { int32_t rc = -1; ocs_textbuf_segment_t *segment; ocs_memset(textbuf, 0, sizeof(*textbuf)); textbuf->ocs = ocs; ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link); segment = ocs_malloc(ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT); if (segment) { segment->buffer = buffer; segment->buffer_length = length; segment->buffer_written = 0; segment->user_allocated = 1; ocs_list_add_tail(&textbuf->segment_list, segment); rc = 0; } return rc; } void ocs_textbuf_free(ocs_t *ocs, ocs_textbuf_t *textbuf) { ocs_textbuf_segment_t *segment; ocs_textbuf_segment_t *n; if (ocs_textbuf_initialized(textbuf)) { ocs_list_foreach_safe(&textbuf->segment_list, segment, n) { ocs_list_remove(&textbuf->segment_list, segment); ocs_textbuf_segment_free(ocs, segment); } ocs_memset(textbuf, 0, sizeof(*textbuf)); } } void ocs_textbuf_printf(ocs_textbuf_t *textbuf, const char *fmt, ...) { va_list ap; if (ocs_textbuf_initialized(textbuf)) { va_start(ap, fmt); ocs_textbuf_vprintf(textbuf, fmt, ap); va_end(ap); } } void ocs_textbuf_vprintf(ocs_textbuf_t *textbuf, const char *fmt, va_list ap) { int avail; int written; ocs_textbuf_segment_t *segment; va_list save_ap; if (!ocs_textbuf_initialized(textbuf)) { return; } va_copy(save_ap, ap); /* fetch last segment */ segment = ocs_list_get_tail(&textbuf->segment_list); avail = ocs_segment_remaining(segment); if (avail == 0) { if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) { goto out; } avail = ocs_segment_remaining(segment); } written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, ap); /* See if data was truncated */ if (written >= avail) { written = avail; if (textbuf->extendable) { /* revert the partially written data */ *(segment->buffer + segment->buffer_written) = 0; /* Allocate a new segment */ if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) { ocs_log_err(textbuf->ocs, "alloc segment failed\n"); goto out; } avail = ocs_segment_remaining(segment); /* Retry the write */ written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, save_ap); } } segment->buffer_written += written; out: va_end(save_ap); } void ocs_textbuf_putc(ocs_textbuf_t *textbuf, uint8_t c) { ocs_textbuf_segment_t *segment; if (ocs_textbuf_initialized(textbuf)) { segment = ocs_list_get_tail(&textbuf->segment_list); if (ocs_segment_remaining(segment)) { *(segment->buffer + segment->buffer_written++) = c; } if (ocs_segment_remaining(segment) == 0) { ocs_textbuf_segment_alloc(textbuf); } } } void ocs_textbuf_puts(ocs_textbuf_t *textbuf, char *s) { if (ocs_textbuf_initialized(textbuf)) { while(*s) { ocs_textbuf_putc(textbuf, *s++); } } } void ocs_textbuf_buffer(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length) { char *s; if (!ocs_textbuf_initialized(textbuf)) { return; } s = (char*) buffer; while(*s) { /* * XML escapes * * " " * ' ' * < < * > > * & & */ switch(*s) { case '"': ocs_textbuf_puts(textbuf, """); break; case '\'': ocs_textbuf_puts(textbuf, "'"); break; case '<': ocs_textbuf_puts(textbuf, "<"); break; case '>': ocs_textbuf_puts(textbuf, ">"); break; case '&': ocs_textbuf_puts(textbuf, "&"); break; default: ocs_textbuf_putc(textbuf, *s); break; } s++; } } void ocs_textbuf_copy(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length) { char *s; if (!ocs_textbuf_initialized(textbuf)) { return; } s = (char*) buffer; while(*s) { ocs_textbuf_putc(textbuf, *s++); } } int32_t ocs_textbuf_remaining(ocs_textbuf_t *textbuf) { if (ocs_textbuf_initialized(textbuf)) { return ocs_segment_remaining(ocs_list_get_head(&textbuf->segment_list)); } else { return 0; } } static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment) { return segment->buffer_length - segment->buffer_written; } void ocs_textbuf_reset(ocs_textbuf_t *textbuf) { uint32_t i = 0; ocs_textbuf_segment_t *segment; ocs_textbuf_segment_t *n; if (ocs_textbuf_initialized(textbuf)) { /* zero written on the first segment, free the rest */ ocs_list_foreach_safe(&textbuf->segment_list, segment, n) { if (i++ == 0) { segment->buffer_written = 0; } else { ocs_list_remove(&textbuf->segment_list, segment); ocs_textbuf_segment_free(textbuf->ocs, segment); } } } } /** * @brief Sparse Vector API. * * This is a trimmed down sparse vector implementation tuned to the problem of * 24-bit FC_IDs. In this case, the 24-bit index value is broken down in three * 8-bit values. These values are used to index up to three 256 element arrays. * Arrays are allocated, only when needed. @n @n * The lookup can complete in constant time (3 indexed array references). @n @n * A typical use case would be that the fabric/directory FC_IDs would cause two rows to be * allocated, and the fabric assigned remote nodes would cause two rows to be allocated, with * the root row always allocated. This gives five rows of 256 x sizeof(void*), * resulting in 10k. */ /** * @ingroup spv * @brief Allocate a new sparse vector row. * * @param os OS handle * @param rowcount Count of rows. * * @par Description * A new sparse vector row is allocated. * * @param rowcount Number of elements in a row. * * @return Returns the pointer to a row. */ static void **spv_new_row(ocs_os_handle_t os, uint32_t rowcount) { return ocs_malloc(os, sizeof(void*) * rowcount, OCS_M_ZERO | OCS_M_NOWAIT); } /** * @ingroup spv * @brief Delete row recursively. * * @par Description * This function recursively deletes the rows in this sparse vector * * @param os OS handle * @param a Pointer to the row. * @param n Number of elements in the row. * @param depth Depth of deleting. * * @return None. */ static void _spv_del(ocs_os_handle_t os, void **a, uint32_t n, uint32_t depth) { if (a) { if (depth) { uint32_t i; for (i = 0; i < n; i ++) { _spv_del(os, a[i], n, depth-1); } ocs_free(os, a, SPV_ROWLEN*sizeof(*a)); } } } /** * @ingroup spv * @brief Delete a sparse vector. * * @par Description * The sparse vector is freed. * * @param spv Pointer to the sparse vector object. */ void spv_del(sparse_vector_t spv) { if (spv) { _spv_del(spv->os, spv->array, SPV_ROWLEN, SPV_DIM); ocs_free(spv->os, spv, sizeof(*spv)); } } /** * @ingroup spv * @brief Instantiate a new sparse vector object. * * @par Description * A new sparse vector is allocated. * * @param os OS handle * * @return Returns the pointer to the sparse vector, or NULL. */ sparse_vector_t spv_new(ocs_os_handle_t os) { sparse_vector_t spv; uint32_t i; spv = ocs_malloc(os, sizeof(*spv), OCS_M_ZERO | OCS_M_NOWAIT); if (!spv) { return NULL; } spv->os = os; spv->max_idx = 1; for (i = 0; i < SPV_DIM; i ++) { spv->max_idx *= SPV_ROWLEN; } return spv; } /** * @ingroup spv * @brief Return the address of a cell. * * @par Description * Returns the address of a cell, allocates sparse rows as needed if the * alloc_new_rows parameter is set. * * @param sv Pointer to the sparse vector. * @param idx Index of which to return the address. * @param alloc_new_rows If TRUE, then new rows may be allocated to set values, * Set to FALSE for retrieving values. * * @return Returns the pointer to the cell, or NULL. */ static void *spv_new_cell(sparse_vector_t sv, uint32_t idx, uint8_t alloc_new_rows) { uint32_t a = (idx >> 16) & 0xff; uint32_t b = (idx >> 8) & 0xff; uint32_t c = (idx >> 0) & 0xff; void **p; if (idx >= sv->max_idx) { return NULL; } if (sv->array == NULL) { sv->array = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); if (sv->array == NULL) { return NULL; } } p = sv->array; if (p[a] == NULL) { p[a] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); if (p[a] == NULL) { return NULL; } } p = p[a]; if (p[b] == NULL) { p[b] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); if (p[b] == NULL) { return NULL; } } p = p[b]; return &p[c]; } /** * @ingroup spv * @brief Set the sparse vector cell value. * * @par Description * Sets the sparse vector at @c idx to @c value. * * @param sv Pointer to the sparse vector. * @param idx Index of which to store. * @param value Value to store. * * @return None. */ void spv_set(sparse_vector_t sv, uint32_t idx, void *value) { void **ref = spv_new_cell(sv, idx, TRUE); if (ref) { *ref = value; } } /** * @ingroup spv * @brief Return the sparse vector cell value. * * @par Description * Returns the value at @c idx. * * @param sv Pointer to the sparse vector. * @param idx Index of which to return the value. * * @return Returns the cell value, or NULL. */ void *spv_get(sparse_vector_t sv, uint32_t idx) { void **ref = spv_new_cell(sv, idx, FALSE); if (ref) { return *ref; } return NULL; } /*****************************************************************/ /* */ /* CRC LOOKUP TABLE */ /* ================ */ /* The following CRC lookup table was generated automagically */ /* by the Rocksoft^tm Model CRC Algorithm Table Generation */ /* Program V1.0 using the following model parameters: */ /* */ /* Width : 2 bytes. */ /* Poly : 0x8BB7 */ /* Reverse : FALSE. */ /* */ /* For more information on the Rocksoft^tm Model CRC Algorithm, */ /* see the document titled "A Painless Guide to CRC Error */ /* Detection Algorithms" by Ross Williams */ /* (ross@guest.adelaide.edu.au.). This document is likely to be */ /* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ /* */ /*****************************************************************/ /* * Emulex Inc, changes: * - minor syntax changes for successful compilation with contemporary * C compilers, and OCS SDK API * - crctable[] generated using Rocksoft public domain code * * Used in the Emulex SDK, the generated file crctable.out is cut and pasted into * applicable SDK sources. */ static unsigned short crctable[256] = { 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 }; /*****************************************************************/ /* End of CRC Lookup Table */ /*****************************************************************/ /** * @brief Calculate the T10 PI CRC guard value for a block. * * Code based on Rocksoft's public domain CRC code, refer to * http://www.ross.net/crc/download/crc_v3.txt. Minimally altered * to work with the ocs_dif API. * * @param blk_adr Pointer to the data buffer. * @param blk_len Number of bytes. * @param crc Previously-calculated CRC, or crcseed for a new block. * * @return Returns the calculated CRC, which may be passed back in for partial blocks. * */ unsigned short t10crc16(const unsigned char *blk_adr, unsigned long blk_len, unsigned short crc) { if (blk_len > 0) { while (blk_len--) { crc = crctable[((crc>>8) ^ *blk_adr++) & 0xFFL] ^ (crc << 8); } } return crc; } struct ocs_ramlog_s { uint32_t initialized; uint32_t textbuf_count; uint32_t textbuf_base; ocs_textbuf_t *textbufs; uint32_t cur_textbuf_idx; ocs_textbuf_t *cur_textbuf; ocs_lock_t lock; }; static uint32_t ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx); /** * @brief Allocate a ramlog buffer. * * Initialize a RAM logging buffer with text buffers totalling buffer_len. * * @param ocs Pointer to driver structure. * @param buffer_len Total length of RAM log buffers. * @param buffer_count Number of text buffers to allocate (totalling buffer-len). * * @return Returns pointer to ocs_ramlog_t instance, or NULL. */ ocs_ramlog_t * ocs_ramlog_init(ocs_t *ocs, uint32_t buffer_len, uint32_t buffer_count) { uint32_t i; uint32_t rc; ocs_ramlog_t *ramlog; ramlog = ocs_malloc(ocs, sizeof(*ramlog), OCS_M_ZERO | OCS_M_NOWAIT); if (ramlog == NULL) { ocs_log_err(ocs, "ocs_malloc ramlog failed\n"); return NULL; } ramlog->textbuf_count = buffer_count; ramlog->textbufs = ocs_malloc(ocs, sizeof(*ramlog->textbufs)*buffer_count, OCS_M_ZERO | OCS_M_NOWAIT); if (ramlog->textbufs == NULL) { ocs_log_err(ocs, "ocs_malloc textbufs failed\n"); ocs_ramlog_free(ocs, ramlog); return NULL; } for (i = 0; i < buffer_count; i ++) { rc = ocs_textbuf_alloc(ocs, &ramlog->textbufs[i], buffer_len); if (rc) { ocs_log_err(ocs, "ocs_textbuf_alloc failed\n"); ocs_ramlog_free(ocs, ramlog); return NULL; } } ramlog->cur_textbuf_idx = 0; ramlog->textbuf_base = 1; ramlog->cur_textbuf = &ramlog->textbufs[0]; ramlog->initialized = TRUE; ocs_lock_init(ocs, &ramlog->lock, "ramlog_lock[%d]", ocs_instance(ocs)); return ramlog; } /** * @brief Free a ramlog buffer. * * A previously allocated RAM logging buffer is freed. * * @param ocs Pointer to driver structure. * @param ramlog Pointer to RAM logging buffer structure. * * @return None. */ void ocs_ramlog_free(ocs_t *ocs, ocs_ramlog_t *ramlog) { uint32_t i; if (ramlog != NULL) { ocs_lock_free(&ramlog->lock); if (ramlog->textbufs) { for (i = 0; i < ramlog->textbuf_count; i ++) { ocs_textbuf_free(ocs, &ramlog->textbufs[i]); } ocs_free(ocs, ramlog->textbufs, ramlog->textbuf_count*sizeof(*ramlog->textbufs)); ramlog->textbufs = NULL; } ocs_free(ocs, ramlog, sizeof(*ramlog)); } } /** * @brief Clear a ramlog buffer. * * The text in the start of day and/or recent ramlog text buffers is cleared. * * @param ocs Pointer to driver structure. * @param ramlog Pointer to RAM logging buffer structure. * @param clear_start_of_day Clear the start of day (driver init) portion of the ramlog. * @param clear_recent Clear the recent messages portion of the ramlog. * * @return None. */ void ocs_ramlog_clear(ocs_t *ocs, ocs_ramlog_t *ramlog, int clear_start_of_day, int clear_recent) { uint32_t i; if (clear_recent) { for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) { ocs_textbuf_reset(&ramlog->textbufs[i]); } ramlog->cur_textbuf_idx = 1; } if (clear_start_of_day && ramlog->textbuf_base) { ocs_textbuf_reset(&ramlog->textbufs[0]); /* Set textbuf_base to 0, so that all buffers are available for * recent logs */ ramlog->textbuf_base = 0; } } /** * @brief Append formatted printf data to a ramlog buffer. * * Formatted data is appended to a RAM logging buffer. * * @param os Pointer to driver structure. * @param fmt Pointer to printf style format specifier. * * @return Returns 0 on success, or a negative error code value on failure. */ int32_t ocs_ramlog_printf(void *os, const char *fmt, ...) { ocs_t *ocs = os; va_list ap; int32_t res; if (ocs == NULL || ocs->ramlog == NULL) { return -1; } va_start(ap, fmt); res = ocs_ramlog_vprintf(ocs->ramlog, fmt, ap); va_end(ap); return res; } /** * @brief Append formatted text to a ramlog using variable arguments. * * Formatted data is appended to the RAM logging buffer, using variable arguments. * * @param ramlog Pointer to RAM logging buffer. * @param fmt Pointer to printf style formatting string. * @param ap Variable argument pointer. * * @return Returns 0 on success, or a negative error code value on failure. */ int32_t ocs_ramlog_vprintf(ocs_ramlog_t *ramlog, const char *fmt, va_list ap) { if (ramlog == NULL || !ramlog->initialized) { return -1; } /* check the current text buffer, if it is almost full (less than 120 characaters), then * roll to the next one. */ ocs_lock(&ramlog->lock); if (ocs_textbuf_remaining(ramlog->cur_textbuf) < 120) { ramlog->cur_textbuf_idx = ocs_ramlog_next_idx(ramlog, ramlog->cur_textbuf_idx); ramlog->cur_textbuf = &ramlog->textbufs[ramlog->cur_textbuf_idx]; ocs_textbuf_reset(ramlog->cur_textbuf); } ocs_textbuf_vprintf(ramlog->cur_textbuf, fmt, ap); ocs_unlock(&ramlog->lock); return 0; } /** * @brief Return next ramlog buffer index. * * Given a RAM logging buffer index, return the next index. * * @param ramlog Pointer to RAM logging buffer. * @param idx Index value. * * @return Returns next index value. */ static uint32_t ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx) { idx = idx + 1; if (idx >= ramlog->textbuf_count) { idx = ramlog->textbuf_base; } return idx; } /** * @brief Perform ramlog buffer driver dump. * * The RAM logging buffer is appended to the driver dump data. * * @param textbuf Pointer to the driver dump text buffer. * @param ramlog Pointer to the RAM logging buffer. * * @return Returns 0 on success, or a negative error code value on failure. */ int32_t ocs_ddump_ramlog(ocs_textbuf_t *textbuf, ocs_ramlog_t *ramlog) { uint32_t i; ocs_textbuf_t *rltextbuf; int idx; if ((ramlog == NULL) || (ramlog->textbufs == NULL)) { return -1; } ocs_ddump_section(textbuf, "driver-log", 0); /* Dump the start of day buffer */ ocs_ddump_section(textbuf, "startofday", 0); /* If textbuf_base is 0, then all buffers are used for recent */ if (ramlog->textbuf_base) { rltextbuf = &ramlog->textbufs[0]; ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf)); } ocs_ddump_endsection(textbuf, "startofday", 0); /* Dump the most recent buffers */ ocs_ddump_section(textbuf, "recent", 0); /* start with the next textbuf */ idx = ocs_ramlog_next_idx(ramlog, ramlog->textbuf_count); for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) { rltextbuf = &ramlog->textbufs[idx]; ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf)); idx = ocs_ramlog_next_idx(ramlog, idx); } ocs_ddump_endsection(textbuf, "recent", 0); ocs_ddump_endsection(textbuf, "driver-log", 0); return 0; } struct ocs_pool_s { ocs_os_handle_t os; ocs_array_t *a; ocs_list_t freelist; uint32_t use_lock:1; ocs_lock_t lock; }; typedef struct { ocs_list_link_t link; } pool_hdr_t; /** * @brief Allocate a memory pool. * * A memory pool of given size and item count is allocated. * * @param os OS handle. * @param size Size in bytes of item. * @param count Number of items in a memory pool. * @param use_lock TRUE to enable locking of pool. * * @return Returns pointer to allocated memory pool, or NULL. */ ocs_pool_t * ocs_pool_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count, uint32_t use_lock) { ocs_pool_t *pool; uint32_t i; pool = ocs_malloc(os, sizeof(*pool), OCS_M_ZERO | OCS_M_NOWAIT); if (pool == NULL) { return NULL; } pool->os = os; pool->use_lock = use_lock; /* Allocate an array where each array item is the size of a pool_hdr_t plus * the requested memory item size (size) */ pool->a = ocs_array_alloc(os, size + sizeof(pool_hdr_t), count); if (pool->a == NULL) { ocs_pool_free(pool); return NULL; } ocs_list_init(&pool->freelist, pool_hdr_t, link); for (i = 0; i < count; i++) { ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i)); } if (pool->use_lock) { ocs_lock_init(os, &pool->lock, "ocs_pool:%p", pool); } return pool; } /** * @brief Reset a memory pool. * * Place all pool elements on the free list, and zero them. * * @param pool Pointer to the pool object. * * @return None. */ void ocs_pool_reset(ocs_pool_t *pool) { uint32_t i; uint32_t count = ocs_array_get_count(pool->a); uint32_t size = ocs_array_get_size(pool->a); if (pool->use_lock) { ocs_lock(&pool->lock); } /* * Remove all the entries from the free list, otherwise we will * encountered linked list asserts when they are re-added. */ while (!ocs_list_empty(&pool->freelist)) { ocs_list_remove_head(&pool->freelist); } /* Reset the free list */ ocs_list_init(&pool->freelist, pool_hdr_t, link); /* Return all elements to the free list and zero the elements */ for (i = 0; i < count; i++) { ocs_memset(ocs_pool_get_instance(pool, i), 0, size - sizeof(pool_hdr_t)); ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i)); } if (pool->use_lock) { ocs_unlock(&pool->lock); } } /** * @brief Free a previously allocated memory pool. * * The memory pool is freed. * * @param pool Pointer to memory pool. * * @return None. */ void ocs_pool_free(ocs_pool_t *pool) { if (pool != NULL) { if (pool->a != NULL) { ocs_array_free(pool->a); } if (pool->use_lock) { ocs_lock_free(&pool->lock); } ocs_free(pool->os, pool, sizeof(*pool)); } } /** * @brief Allocate a memory pool item * * A memory pool item is taken from the free list and returned. * * @param pool Pointer to memory pool. * * @return Pointer to allocated item, otherwise NULL if there are no unallocated * items. */ void * ocs_pool_get(ocs_pool_t *pool) { pool_hdr_t *h; void *item = NULL; if (pool->use_lock) { ocs_lock(&pool->lock); } h = ocs_list_remove_head(&pool->freelist); if (h != NULL) { /* Return the array item address offset by the size of pool_hdr_t */ item = &h[1]; } if (pool->use_lock) { ocs_unlock(&pool->lock); } return item; } /** * @brief free memory pool item * * A memory pool item is freed. * * @param pool Pointer to memory pool. * @param item Pointer to item to free. * * @return None. */ void ocs_pool_put(ocs_pool_t *pool, void *item) { pool_hdr_t *h; if (pool->use_lock) { ocs_lock(&pool->lock); } /* Fetch the address of the array item, which is the item address negatively offset * by size of pool_hdr_t (note the index of [-1] */ h = &((pool_hdr_t*)item)[-1]; ocs_list_add_tail(&pool->freelist, h); if (pool->use_lock) { ocs_unlock(&pool->lock); } } /** * @brief Return memory pool item count. * * Returns the allocated number of items. * * @param pool Pointer to memory pool. * * @return Returns count of allocated items. */ uint32_t ocs_pool_get_count(ocs_pool_t *pool) { uint32_t count; if (pool->use_lock) { ocs_lock(&pool->lock); } count = ocs_array_get_count(pool->a); if (pool->use_lock) { ocs_unlock(&pool->lock); } return count; } /** * @brief Return item given an index. * * A pointer to a memory pool item is returned given an index. * * @param pool Pointer to memory pool. * @param idx Index. * * @return Returns pointer to item, or NULL if index is invalid. */ void * ocs_pool_get_instance(ocs_pool_t *pool, uint32_t idx) { pool_hdr_t *h = ocs_array_get(pool->a, idx); if (h == NULL) { return NULL; } return &h[1]; } /** * @brief Return count of free objects in a pool. * * The number of objects on a pool's free list. * * @param pool Pointer to memory pool. * * @return Returns count of objects on free list. */ uint32_t ocs_pool_get_freelist_count(ocs_pool_t *pool) { uint32_t count = 0; void *item; if (pool->use_lock) { ocs_lock(&pool->lock); } ocs_list_foreach(&pool->freelist, item) { count++; } if (pool->use_lock) { ocs_unlock(&pool->lock); } return count; }