freebsd-nq/sys/dev/ocs_fc/ocs_utils.c
Kenneth D. Merry ef270ab1b6 Bring in the Broadcom/Emulex Fibre Channel driver, ocs_fc(4).
The ocs_fc(4) driver supports the following hardware:

Emulex 16/8G FC GEN 5 HBAS
	LPe15004 FC Host Bus Adapters
	LPe160XX FC Host Bus Adapters

Emulex 32/16G FC GEN 6 HBAS
	LPe3100X FC Host Bus Adapters
	LPe3200X FC Host Bus Adapters

The driver supports target and initiator mode, and also supports FC-Tape.

Note that the driver only currently works on little endian platforms.  It
is only included in the module build for amd64 and i386, and in GENERIC
on amd64 only.

Submitted by:	Ram Kishore Vegesna <ram.vegesna@broadcom.com>
Reviewed by:	mav
MFC after:	5 days
Relnotes:	yes
Sponsored by:	Broadcom
Differential Revision:	https://reviews.freebsd.org/D11423
2018-03-30 15:28:25 +00:00

2827 lines
70 KiB
C

/*-
* 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, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\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, "</%s>\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</%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, "</%s>\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, "<qentries>\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, "</qentries>\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 <b>mask</b> 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 <b>mask</b> 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
*
* <description>
*
* @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
*
* " &quot;
* ' &apos;
* < &lt;
* > &gt;
* & &amp;
*/
switch(*s) {
case '"': ocs_textbuf_puts(textbuf, "&quot;"); break;
case '\'': ocs_textbuf_puts(textbuf, "&apos;"); break;
case '<': ocs_textbuf_puts(textbuf, "&lt;"); break;
case '>': ocs_textbuf_puts(textbuf, "&gt;"); break;
case '&': ocs_textbuf_puts(textbuf, "&amp;"); 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;
}