1 /*- 2 * Copyright (c) 2017 Broadcom. All rights reserved. 3 * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $FreeBSD$ 32 */ 33 34 /** 35 * @file 36 * 37 */ 38 39 #include "ocs.h" 40 #include "ocs_os.h" 41 42 #define DEFAULT_SLAB_LEN (64*1024) 43 44 struct ocs_array_s { 45 ocs_os_handle_t os; 46 47 uint32_t size; 48 uint32_t count; 49 50 uint32_t n_rows; 51 uint32_t elems_per_row; 52 uint32_t bytes_per_row; 53 54 void **array_rows; 55 uint32_t array_rows_len; 56 }; 57 58 static uint32_t slab_len = DEFAULT_SLAB_LEN; 59 60 /** 61 * @brief Set array slab allocation length 62 * 63 * The slab length is the maximum allocation length that the array uses. 64 * The default 64k slab length may be overridden using this function. 65 * 66 * @param len new slab length. 67 * 68 * @return none 69 */ 70 void 71 ocs_array_set_slablen(uint32_t len) 72 { 73 slab_len = len; 74 } 75 76 /** 77 * @brief Allocate an array object 78 * 79 * An array object of size and number of elements is allocated 80 * 81 * @param os OS handle 82 * @param size size of array elements in bytes 83 * @param count number of elements in array 84 * 85 * @return pointer to array object or NULL 86 */ 87 ocs_array_t * 88 ocs_array_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count) 89 { 90 ocs_array_t *array = NULL; 91 uint32_t i; 92 93 /* Fail if the item size exceeds slab_len - caller should increase slab_size, 94 * or not use this API. 95 */ 96 if (size > slab_len) { 97 ocs_log_err(NULL, "Error: size exceeds slab length\n"); 98 return NULL; 99 } 100 101 array = ocs_malloc(os, sizeof(*array), OCS_M_ZERO | OCS_M_NOWAIT); 102 if (array == NULL) { 103 return NULL; 104 } 105 106 array->os = os; 107 array->size = size; 108 array->count = count; 109 array->elems_per_row = slab_len / size; 110 array->n_rows = (count + array->elems_per_row - 1) / array->elems_per_row; 111 array->bytes_per_row = array->elems_per_row * array->size; 112 113 array->array_rows_len = array->n_rows * sizeof(*array->array_rows); 114 array->array_rows = ocs_malloc(os, array->array_rows_len, OCS_M_ZERO | OCS_M_NOWAIT); 115 if (array->array_rows == NULL) { 116 ocs_array_free(array); 117 return NULL; 118 } 119 for (i = 0; i < array->n_rows; i++) { 120 array->array_rows[i] = ocs_malloc(os, array->bytes_per_row, OCS_M_ZERO | OCS_M_NOWAIT); 121 if (array->array_rows[i] == NULL) { 122 ocs_array_free(array); 123 return NULL; 124 } 125 } 126 127 return array; 128 } 129 130 /** 131 * @brief Free an array object 132 * 133 * Frees a prevously allocated array object 134 * 135 * @param array pointer to array object 136 * 137 * @return none 138 */ 139 void 140 ocs_array_free(ocs_array_t *array) 141 { 142 uint32_t i; 143 144 if (array != NULL) { 145 if (array->array_rows != NULL) { 146 for (i = 0; i < array->n_rows; i++) { 147 if (array->array_rows[i] != NULL) { 148 ocs_free(array->os, array->array_rows[i], array->bytes_per_row); 149 } 150 } 151 ocs_free(array->os, array->array_rows, array->array_rows_len); 152 } 153 ocs_free(array->os, array, sizeof(*array)); 154 } 155 } 156 157 /** 158 * @brief Return reference to an element of an array object 159 * 160 * Return the address of an array element given an index 161 * 162 * @param array pointer to array object 163 * @param idx array element index 164 * 165 * @return rointer to array element, or NULL if index out of range 166 */ 167 void *ocs_array_get(ocs_array_t *array, uint32_t idx) 168 { 169 void *entry = NULL; 170 171 if (idx < array->count) { 172 uint32_t row = idx / array->elems_per_row; 173 uint32_t offset = idx % array->elems_per_row; 174 entry = ((uint8_t*)array->array_rows[row]) + (offset * array->size); 175 } 176 return entry; 177 } 178 179 /** 180 * @brief Return number of elements in an array 181 * 182 * Return the number of elements in an array 183 * 184 * @param array pointer to array object 185 * 186 * @return returns count of elements in an array 187 */ 188 uint32_t 189 ocs_array_get_count(ocs_array_t *array) 190 { 191 return array->count; 192 } 193 194 /** 195 * @brief Return size of array elements in bytes 196 * 197 * Returns the size in bytes of each array element 198 * 199 * @param array pointer to array object 200 * 201 * @return size of array element 202 */ 203 uint32_t 204 ocs_array_get_size(ocs_array_t *array) 205 { 206 return array->size; 207 } 208 209 /** 210 * @brief Void pointer array structure 211 * 212 * This structure describes an object consisting of an array of void 213 * pointers. The object is allocated with a maximum array size, entries 214 * are then added to the array with while maintaining an entry count. A set of 215 * iterator APIs are included to allow facilitate cycling through the array 216 * entries in a circular fashion. 217 * 218 */ 219 struct ocs_varray_s { 220 ocs_os_handle_t os; 221 uint32_t array_count; /*>> maximum entry count in array */ 222 void **array; /*>> pointer to allocated array memory */ 223 uint32_t entry_count; /*>> number of entries added to the array */ 224 uint32_t next_index; /*>> iterator next index */ 225 ocs_lock_t lock; /*>> iterator lock */ 226 }; 227 228 /** 229 * @brief Allocate a void pointer array 230 * 231 * A void pointer array of given length is allocated. 232 * 233 * @param os OS handle 234 * @param array_count Array size 235 * 236 * @return returns a pointer to the ocs_varray_t object, other NULL on error 237 */ 238 ocs_varray_t * 239 ocs_varray_alloc(ocs_os_handle_t os, uint32_t array_count) 240 { 241 ocs_varray_t *va; 242 243 va = ocs_malloc(os, sizeof(*va), OCS_M_ZERO | OCS_M_NOWAIT); 244 if (va != NULL) { 245 va->os = os; 246 va->array_count = array_count; 247 va->array = ocs_malloc(os, sizeof(*va->array) * va->array_count, OCS_M_ZERO | OCS_M_NOWAIT); 248 if (va->array != NULL) { 249 va->next_index = 0; 250 ocs_lock_init(os, &va->lock, "varray:%p", va); 251 } else { 252 ocs_free(os, va, sizeof(*va)); 253 va = NULL; 254 } 255 } 256 return va; 257 } 258 259 /** 260 * @brief Free a void pointer array 261 * 262 * The void pointer array object is free'd 263 * 264 * @param va Pointer to void pointer array 265 * 266 * @return none 267 */ 268 void 269 ocs_varray_free(ocs_varray_t *va) 270 { 271 if (va != NULL) { 272 ocs_lock_free(&va->lock); 273 if (va->array != NULL) { 274 ocs_free(va->os, va->array, sizeof(*va->array) * va->array_count); 275 } 276 ocs_free(va->os, va, sizeof(*va)); 277 } 278 } 279 280 /** 281 * @brief Add an entry to a void pointer array 282 * 283 * An entry is added to the void pointer array 284 * 285 * @param va Pointer to void pointer array 286 * @param entry Pointer to entry to add 287 * 288 * @return returns 0 if entry was added, -1 if there is no more space in the array 289 */ 290 int32_t 291 ocs_varray_add(ocs_varray_t *va, void *entry) 292 { 293 uint32_t rc = -1; 294 295 ocs_lock(&va->lock); 296 if (va->entry_count < va->array_count) { 297 va->array[va->entry_count++] = entry; 298 rc = 0; 299 } 300 ocs_unlock(&va->lock); 301 302 return rc; 303 } 304 305 /** 306 * @brief Reset the void pointer array iterator 307 * 308 * The next index value of the void pointer array iterator is cleared. 309 * 310 * @param va Pointer to void pointer array 311 * 312 * @return none 313 */ 314 void 315 ocs_varray_iter_reset(ocs_varray_t *va) 316 { 317 ocs_lock(&va->lock); 318 va->next_index = 0; 319 ocs_unlock(&va->lock); 320 } 321 322 /** 323 * @brief Return next entry from a void pointer array 324 * 325 * The next entry in the void pointer array is returned. 326 * 327 * @param va Pointer to void point array 328 * 329 * Note: takes the void pointer array lock 330 * 331 * @return returns next void pointer entry 332 */ 333 void * 334 ocs_varray_iter_next(ocs_varray_t *va) 335 { 336 void *rval = NULL; 337 338 if (va != NULL) { 339 ocs_lock(&va->lock); 340 rval = _ocs_varray_iter_next(va); 341 ocs_unlock(&va->lock); 342 } 343 return rval; 344 } 345 346 /** 347 * @brief Return next entry from a void pointer array 348 * 349 * The next entry in the void pointer array is returned. 350 * 351 * @param va Pointer to void point array 352 * 353 * Note: doesn't take the void pointer array lock 354 * 355 * @return returns next void pointer entry 356 */ 357 void * 358 _ocs_varray_iter_next(ocs_varray_t *va) 359 { 360 void *rval; 361 362 rval = va->array[va->next_index]; 363 if (++va->next_index >= va->entry_count) { 364 va->next_index = 0; 365 } 366 return rval; 367 } 368 369 /** 370 * @brief Take void pointer array lock 371 * 372 * Takes the lock for the given void pointer array 373 * 374 * @param va Pointer to void pointer array 375 * 376 * @return none 377 */ 378 void 379 ocs_varray_lock(ocs_varray_t *va) 380 { 381 ocs_lock(&va->lock); 382 } 383 384 /** 385 * @brief Release void pointer array lock 386 * 387 * Releases the lock for the given void pointer array 388 * 389 * @param va Pointer to void pointer array 390 * 391 * @return none 392 */ 393 void 394 ocs_varray_unlock(ocs_varray_t *va) 395 { 396 ocs_unlock(&va->lock); 397 } 398 399 /** 400 * @brief Return entry count for a void pointer array 401 * 402 * The entry count for a void pointer array is returned 403 * 404 * @param va Pointer to void pointer array 405 * 406 * @return returns entry count 407 */ 408 uint32_t 409 ocs_varray_get_count(ocs_varray_t *va) 410 { 411 uint32_t rc; 412 413 ocs_lock(&va->lock); 414 rc = va->entry_count; 415 ocs_unlock(&va->lock); 416 return rc; 417 } 418 419 420 struct ocs_cbuf_s { 421 ocs_os_handle_t os; /*<< OS handle */ 422 uint32_t entry_count; /*<< entry count */ 423 void **array; /*<< pointer to array of cbuf pointers */ 424 uint32_t pidx; /*<< producer index */ 425 uint32_t cidx; /*<< consumer index */ 426 ocs_lock_t cbuf_plock; /*<< idx lock */ 427 ocs_lock_t cbuf_clock; /*<< idx lock */ 428 ocs_sem_t cbuf_psem; /*<< cbuf producer counting semaphore */ 429 ocs_sem_t cbuf_csem; /*<< cbuf consumer counting semaphore */ 430 }; 431 432 /** 433 * @brief Initialize a circular buffer queue 434 * 435 * A circular buffer with producer/consumer API is allocated 436 * 437 * @param os OS handle 438 * @param entry_count count of entries 439 * 440 * @return returns pointer to circular buffer, or NULL 441 */ 442 ocs_cbuf_t* 443 ocs_cbuf_alloc(ocs_os_handle_t os, uint32_t entry_count) 444 { 445 ocs_cbuf_t *cbuf; 446 447 cbuf = ocs_malloc(os, sizeof(*cbuf), OCS_M_NOWAIT | OCS_M_ZERO); 448 if (cbuf == NULL) { 449 return NULL; 450 } 451 452 cbuf->os = os; 453 cbuf->entry_count = entry_count; 454 cbuf->pidx = 0; 455 cbuf->cidx = 0; 456 457 ocs_lock_init(NULL, &cbuf->cbuf_clock, "cbuf_c:%p", cbuf); 458 ocs_lock_init(NULL, &cbuf->cbuf_plock, "cbuf_p:%p", cbuf); 459 ocs_sem_init(&cbuf->cbuf_csem, 0, "cbuf:%p", cbuf); 460 ocs_sem_init(&cbuf->cbuf_psem, cbuf->entry_count, "cbuf:%p", cbuf); 461 462 cbuf->array = ocs_malloc(os, entry_count * sizeof(*cbuf->array), OCS_M_NOWAIT | OCS_M_ZERO); 463 if (cbuf->array == NULL) { 464 ocs_cbuf_free(cbuf); 465 return NULL; 466 } 467 468 return cbuf; 469 } 470 471 /** 472 * @brief Free a circular buffer 473 * 474 * The memory resources of a circular buffer are free'd 475 * 476 * @param cbuf pointer to circular buffer 477 * 478 * @return none 479 */ 480 void 481 ocs_cbuf_free(ocs_cbuf_t *cbuf) 482 { 483 if (cbuf != NULL) { 484 if (cbuf->array != NULL) { 485 ocs_free(cbuf->os, cbuf->array, sizeof(*cbuf->array) * cbuf->entry_count); 486 } 487 ocs_lock_free(&cbuf->cbuf_clock); 488 ocs_lock_free(&cbuf->cbuf_plock); 489 ocs_free(cbuf->os, cbuf, sizeof(*cbuf)); 490 } 491 } 492 493 /** 494 * @brief Get pointer to buffer 495 * 496 * Wait for a buffer to become available, and return a pointer to the buffer. 497 * 498 * @param cbuf pointer to circular buffer 499 * @param timeout_usec timeout in microseconds 500 * 501 * @return pointer to buffer, or NULL if timeout 502 */ 503 void* 504 ocs_cbuf_get(ocs_cbuf_t *cbuf, int32_t timeout_usec) 505 { 506 void *ret = NULL; 507 508 if (likely(ocs_sem_p(&cbuf->cbuf_csem, timeout_usec) == 0)) { 509 ocs_lock(&cbuf->cbuf_clock); 510 ret = cbuf->array[cbuf->cidx]; 511 if (unlikely(++cbuf->cidx >= cbuf->entry_count)) { 512 cbuf->cidx = 0; 513 } 514 ocs_unlock(&cbuf->cbuf_clock); 515 ocs_sem_v(&cbuf->cbuf_psem); 516 } 517 return ret; 518 } 519 520 /** 521 * @brief write a buffer 522 * 523 * The buffer is written to the circular buffer. 524 * 525 * @param cbuf pointer to circular buffer 526 * @param elem pointer to entry 527 * 528 * @return returns 0 for success, a negative error code value for failure. 529 */ 530 int32_t 531 ocs_cbuf_put(ocs_cbuf_t *cbuf, void *elem) 532 { 533 int32_t rc = 0; 534 535 if (likely(ocs_sem_p(&cbuf->cbuf_psem, -1) == 0)) { 536 ocs_lock(&cbuf->cbuf_plock); 537 cbuf->array[cbuf->pidx] = elem; 538 if (unlikely(++cbuf->pidx >= cbuf->entry_count)) { 539 cbuf->pidx = 0; 540 } 541 ocs_unlock(&cbuf->cbuf_plock); 542 ocs_sem_v(&cbuf->cbuf_csem); 543 } else { 544 rc = -1; 545 } 546 return rc; 547 } 548 549 /** 550 * @brief Prime a circular buffer data 551 * 552 * Post array buffers to a circular buffer 553 * 554 * @param cbuf pointer to circular buffer 555 * @param array pointer to buffer array 556 * 557 * @return returns 0 for success, a negative error code value for failure. 558 */ 559 int32_t 560 ocs_cbuf_prime(ocs_cbuf_t *cbuf, ocs_array_t *array) 561 { 562 uint32_t i; 563 uint32_t count = MIN(ocs_array_get_count(array), cbuf->entry_count); 564 565 for (i = 0; i < count; i++) { 566 ocs_cbuf_put(cbuf, ocs_array_get(array, i)); 567 } 568 return 0; 569 } 570 571 /** 572 * @brief Generate driver dump start of file information 573 * 574 * The start of file information is added to 'textbuf' 575 * 576 * @param textbuf pointer to driver dump text buffer 577 * 578 * @return none 579 */ 580 581 void 582 ocs_ddump_startfile(ocs_textbuf_t *textbuf) 583 { 584 ocs_textbuf_printf(textbuf, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n"); 585 } 586 587 /** 588 * @brief Generate driver dump end of file information 589 * 590 * The end of file information is added to 'textbuf' 591 * 592 * @param textbuf pointer to driver dump text buffer 593 * 594 * @return none 595 */ 596 597 void 598 ocs_ddump_endfile(ocs_textbuf_t *textbuf) 599 { 600 } 601 602 /** 603 * @brief Generate driver dump section start data 604 * 605 * The driver section start information is added to textbuf 606 * 607 * @param textbuf pointer to text buffer 608 * @param name name of section 609 * @param instance instance number of this section 610 * 611 * @return none 612 */ 613 614 void 615 ocs_ddump_section(ocs_textbuf_t *textbuf, const char *name, uint32_t instance) 616 { 617 ocs_textbuf_printf(textbuf, "<%s type=\"section\" instance=\"%d\">\n", name, instance); 618 } 619 620 /** 621 * @brief Generate driver dump section end data 622 * 623 * The driver section end information is added to textbuf 624 * 625 * @param textbuf pointer to text buffer 626 * @param name name of section 627 * @param instance instance number of this section 628 * 629 * @return none 630 */ 631 632 void 633 ocs_ddump_endsection(ocs_textbuf_t *textbuf, const char *name, uint32_t instance) 634 { 635 ocs_textbuf_printf(textbuf, "</%s>\n", name); 636 } 637 638 /** 639 * @brief Generate driver dump data for a given value 640 * 641 * A value is added to textbuf 642 * 643 * @param textbuf pointer to text buffer 644 * @param name name of variable 645 * @param fmt snprintf format specifier 646 * 647 * @return none 648 */ 649 650 void 651 ocs_ddump_value(ocs_textbuf_t *textbuf, const char *name, const char *fmt, ...) 652 { 653 va_list ap; 654 char valuebuf[64]; 655 656 va_start(ap, fmt); 657 vsnprintf(valuebuf, sizeof(valuebuf), fmt, ap); 658 va_end(ap); 659 660 ocs_textbuf_printf(textbuf, "<%s>%s</%s>\n", name, valuebuf, name); 661 } 662 663 664 /** 665 * @brief Generate driver dump data for an arbitrary buffer of DWORDS 666 * 667 * A status value is added to textbuf 668 * 669 * @param textbuf pointer to text buffer 670 * @param name name of status variable 671 * @param instance instance number of this section 672 * @param buffer buffer to print 673 * @param size size of buffer in bytes 674 * 675 * @return none 676 */ 677 678 void 679 ocs_ddump_buffer(ocs_textbuf_t *textbuf, const char *name, uint32_t instance, void *buffer, uint32_t size) 680 { 681 uint32_t *dword; 682 uint32_t i; 683 uint32_t count; 684 685 count = size / sizeof(uint32_t); 686 687 if (count == 0) { 688 return; 689 } 690 691 ocs_textbuf_printf(textbuf, "<%s type=\"buffer\" instance=\"%d\">\n", name, instance); 692 693 dword = buffer; 694 for (i = 0; i < count; i++) { 695 #define OCS_NEWLINE_MOD 8 696 ocs_textbuf_printf(textbuf, "%08x ", *dword++); 697 if ((i % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1)) { 698 ocs_textbuf_printf(textbuf, "\n"); 699 } 700 } 701 702 ocs_textbuf_printf(textbuf, "</%s>\n", name); 703 } 704 705 /** 706 * @brief Generate driver dump for queue 707 * 708 * Add queue elements to text buffer 709 * 710 * @param textbuf pointer to driver dump text buffer 711 * @param q_addr address of start of queue 712 * @param size size of each queue entry 713 * @param length number of queue entries in the queue 714 * @param index current index of queue 715 * @param qentries number of most recent queue entries to dump 716 * 717 * @return none 718 */ 719 720 void 721 ocs_ddump_queue_entries(ocs_textbuf_t *textbuf, void *q_addr, uint32_t size, 722 uint32_t length, int32_t index, uint32_t qentries) 723 { 724 uint32_t i; 725 uint32_t j; 726 uint8_t *entry; 727 uint32_t *dword; 728 uint32_t entry_count = 0; 729 uint32_t entry_words = size / sizeof(uint32_t); 730 731 if ((qentries == (uint32_t)-1) || (qentries > length)) { 732 /* if qentries is -1 or larger than queue size, dump entire queue */ 733 entry_count = length; 734 index = 0; 735 } else { 736 entry_count = qentries; 737 738 index -= (qentries - 1); 739 if (index < 0) { 740 index += length; 741 } 742 743 } 744 #define OCS_NEWLINE_MOD 8 745 ocs_textbuf_printf(textbuf, "<qentries>\n"); 746 for (i = 0; i < entry_count; i++){ 747 entry = q_addr; 748 entry += index * size; 749 dword = (uint32_t *)entry; 750 751 ocs_textbuf_printf(textbuf, "[%04x] ", index); 752 for (j = 0; j < entry_words; j++) { 753 ocs_textbuf_printf(textbuf, "%08x ", *dword++); 754 if (((j+1) == entry_words) || 755 ((j % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1))) { 756 ocs_textbuf_printf(textbuf, "\n"); 757 if ((j+1) < entry_words) { 758 ocs_textbuf_printf(textbuf, " "); 759 } 760 } 761 } 762 763 index++; 764 if ((uint32_t)index >= length) { 765 index = 0; 766 } 767 } 768 ocs_textbuf_printf(textbuf, "</qentries>\n"); 769 } 770 771 772 #define OCS_DEBUG_ENABLE(x) (x ? ~0 : 0) 773 774 #define OCS_DEBUG_MASK \ 775 (OCS_DEBUG_ENABLE(1) & OCS_DEBUG_ALWAYS) | \ 776 (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_MQ_DUMP) | \ 777 (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_CQ_DUMP) | \ 778 (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_WQ_DUMP) | \ 779 (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_EQ_DUMP) | \ 780 (OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_SPARAM_DUMP) 781 782 static uint32_t ocs_debug_mask = OCS_DEBUG_MASK; 783 784 static int 785 _isprint(int c) { 786 return ((c > 32) && (c < 127)); 787 } 788 789 /** 790 * @ingroup debug 791 * @brief enable debug options 792 * 793 * Enables debug options by or-ing in <b>mask</b> into the currently enabled 794 * debug mask. 795 * 796 * @param mask mask bits to enable 797 * 798 * @return none 799 */ 800 801 void ocs_debug_enable(uint32_t mask) { 802 ocs_debug_mask |= mask; 803 } 804 805 /** 806 * @ingroup debug 807 * @brief disable debug options 808 * 809 * Disables debug options by clearing bits in <b>mask</b> into the currently enabled 810 * debug mask. 811 * 812 * @param mask mask bits to enable 813 * 814 * @return none 815 */ 816 817 void ocs_debug_disable(uint32_t mask) { 818 ocs_debug_mask &= ~mask; 819 } 820 821 /** 822 * @ingroup debug 823 * @brief return true if debug bits are enabled 824 * 825 * Returns true if the request debug bits are set. 826 * 827 * @param mask debug bit mask 828 * 829 * @return true if corresponding bits are set 830 * 831 * @note Passing in a mask value of zero always returns true 832 */ 833 834 int ocs_debug_is_enabled(uint32_t mask) { 835 return (ocs_debug_mask & mask) == mask; 836 } 837 838 839 /** 840 * @ingroup debug 841 * @brief Dump 32 bit hex/ascii data 842 * 843 * Dumps using ocs_log a buffer of data as 32 bit hex and ascii 844 * 845 * @param mask debug enable bits 846 * @param os os handle 847 * @param label text label for the display (may be NULL) 848 * @param buf pointer to data buffer 849 * @param buf_length length of data buffer 850 * 851 * @return none 852 * 853 */ 854 855 void 856 ocs_dump32(uint32_t mask, ocs_os_handle_t os, const char *label, void *buf, uint32_t buf_length) 857 { 858 uint32_t word_count = buf_length / sizeof(uint32_t); 859 uint32_t i; 860 uint32_t columns = 8; 861 uint32_t n; 862 uint32_t *wbuf; 863 char *cbuf; 864 uint32_t addr = 0; 865 char linebuf[200]; 866 char *pbuf = linebuf; 867 868 if (!ocs_debug_is_enabled(mask)) 869 return; 870 871 if (label) 872 ocs_log_debug(os, "%s\n", label); 873 874 wbuf = buf; 875 while (word_count > 0) { 876 pbuf = linebuf; 877 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X: ", addr); 878 879 n = word_count; 880 if (n > columns) 881 n = columns; 882 883 for (i = 0; i < n; i ++) 884 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X ", wbuf[i]); 885 886 for (; i < columns; i ++) 887 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%8s ", ""); 888 889 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), " "); 890 cbuf = (char*)wbuf; 891 for (i = 0; i < n*sizeof(uint32_t); i ++) 892 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%c", _isprint(cbuf[i]) ? cbuf[i] : '.'); 893 pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "\n"); 894 895 ocs_log_debug(os, "%s", linebuf); 896 897 wbuf += n; 898 word_count -= n; 899 addr += n*sizeof(uint32_t); 900 } 901 } 902 903 904 #if defined(OCS_DEBUG_QUEUE_HISTORY) 905 906 /* each bit corresponds to word to capture */ 907 #define OCS_Q_HIST_WQE_WORD_MASK_DEFAULT (BIT(4) | BIT(6) | BIT(7) | BIT(9) | BIT(12)) 908 #define OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(12)) 909 #define OCS_Q_HIST_IWRITE_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9)) 910 #define OCS_Q_HIST_IREAD_WQE_WORD_MASK (BIT(4) | BIT(6) | BIT(7) | BIT(9)) 911 #define OCS_Q_HIST_ABORT_WQE_WORD_MASK (BIT(3) | BIT(7) | BIT(8) | BIT(9)) 912 #define OCS_Q_HIST_WCQE_WORD_MASK (BIT(0) | BIT(3)) 913 #define OCS_Q_HIST_WCQE_WORD_MASK_ERR (BIT(0) | BIT(1) | BIT(2) | BIT(3)) 914 #define OCS_Q_HIST_CQXABT_WORD_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) 915 916 /* if set, will provide extra queue information in each entry */ 917 #define OCS_Q_HIST_ENABLE_Q_INFO 0 918 uint8_t ocs_queue_history_q_info_enabled(void) 919 { 920 return OCS_Q_HIST_ENABLE_Q_INFO; 921 } 922 923 /* if set, will provide timestamps in each entry */ 924 #define OCS_Q_HIST_ENABLE_TIMESTAMPS 0 925 uint8_t ocs_queue_history_timestamp_enabled(void) 926 { 927 return OCS_Q_HIST_ENABLE_TIMESTAMPS; 928 } 929 930 /* Add WQEs and masks to override default WQE mask */ 931 ocs_q_hist_wqe_mask_t ocs_q_hist_wqe_masks[] = { 932 /* WQE command Word mask */ 933 {SLI4_WQE_ABORT, OCS_Q_HIST_ABORT_WQE_WORD_MASK}, 934 {SLI4_WQE_FCP_IREAD64, OCS_Q_HIST_IREAD_WQE_WORD_MASK}, 935 {SLI4_WQE_FCP_IWRITE64, OCS_Q_HIST_IWRITE_WQE_WORD_MASK}, 936 {SLI4_WQE_FCP_CONT_TRECEIVE64, OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK}, 937 }; 938 939 /* CQE masks */ 940 ocs_q_hist_cqe_mask_t ocs_q_hist_cqe_masks[] = { 941 /* CQE type Q_hist_type mask (success) mask (non-success) */ 942 {SLI_QENTRY_WQ, OCS_Q_HIST_TYPE_CWQE, OCS_Q_HIST_WCQE_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK_ERR}, 943 {SLI_QENTRY_XABT, OCS_Q_HIST_TYPE_CXABT, OCS_Q_HIST_CQXABT_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK}, 944 }; 945 946 static uint32_t ocs_q_hist_get_wqe_mask(sli4_generic_wqe_t *wqe) 947 { 948 uint32_t i; 949 for (i = 0; i < ARRAY_SIZE(ocs_q_hist_wqe_masks); i++) { 950 if (ocs_q_hist_wqe_masks[i].command == wqe->command) { 951 return ocs_q_hist_wqe_masks[i].mask; 952 } 953 } 954 /* return default WQE mask */ 955 return OCS_Q_HIST_WQE_WORD_MASK_DEFAULT; 956 } 957 958 /** 959 * @ingroup debug 960 * @brief Initialize resources for queue history 961 * 962 * @param os os handle 963 * @param q_hist Pointer to the queue history object. 964 * 965 * @return none 966 */ 967 void 968 ocs_queue_history_init(ocs_t *ocs, ocs_hw_q_hist_t *q_hist) 969 { 970 q_hist->ocs = ocs; 971 if (q_hist->q_hist != NULL) { 972 /* Setup is already done */ 973 ocs_log_debug(ocs, "q_hist not NULL, skipping init\n"); 974 return; 975 } 976 977 q_hist->q_hist = ocs_malloc(ocs, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE, OCS_M_ZERO | OCS_M_NOWAIT); 978 979 if (q_hist->q_hist == NULL) { 980 ocs_log_err(ocs, "Could not allocate queue history buffer\n"); 981 } else { 982 ocs_lock_init(ocs, &q_hist->q_hist_lock, "queue history lock[%d]", ocs_instance(ocs)); 983 } 984 985 q_hist->q_hist_index = 0; 986 } 987 988 /** 989 * @ingroup debug 990 * @brief Free resources for queue history 991 * 992 * @param q_hist Pointer to the queue history object. 993 * 994 * @return none 995 */ 996 void 997 ocs_queue_history_free(ocs_hw_q_hist_t *q_hist) 998 { 999 ocs_t *ocs = q_hist->ocs; 1000 1001 if (q_hist->q_hist != NULL) { 1002 ocs_free(ocs, q_hist->q_hist, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE); 1003 ocs_lock_free(&q_hist->q_hist_lock); 1004 q_hist->q_hist = NULL; 1005 } 1006 } 1007 1008 static void 1009 ocs_queue_history_add_q_info(ocs_hw_q_hist_t *q_hist, uint32_t qid, uint32_t qindex) 1010 { 1011 if (ocs_queue_history_q_info_enabled()) { 1012 /* write qid, index */ 1013 q_hist->q_hist[q_hist->q_hist_index] = (qid << 16) | qindex; 1014 q_hist->q_hist_index++; 1015 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1016 } 1017 } 1018 1019 static void 1020 ocs_queue_history_add_timestamp(ocs_hw_q_hist_t *q_hist) 1021 { 1022 if (ocs_queue_history_timestamp_enabled()) { 1023 /* write tsc */ 1024 uint64_t tsc_value; 1025 tsc_value = get_cyclecount(); 1026 q_hist->q_hist[q_hist->q_hist_index] = ((tsc_value >> 32 ) & 0xFFFFFFFF); 1027 q_hist->q_hist_index++; 1028 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1029 q_hist->q_hist[q_hist->q_hist_index] = (tsc_value & 0xFFFFFFFF); 1030 q_hist->q_hist_index++; 1031 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1032 } 1033 } 1034 1035 /** 1036 * @ingroup debug 1037 * @brief Log work queue entry (WQE) into history array 1038 * 1039 * @param q_hist Pointer to the queue history object. 1040 * @param entryw Work queue entry in words 1041 * @param qid Queue ID 1042 * @param qindex Queue index 1043 * 1044 * @return none 1045 */ 1046 void 1047 ocs_queue_history_wq(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t qid, uint32_t qindex) 1048 { 1049 int i; 1050 ocs_q_hist_ftr_t ftr; 1051 uint32_t wqe_word_mask = ocs_q_hist_get_wqe_mask((sli4_generic_wqe_t *)entryw); 1052 1053 if (q_hist->q_hist == NULL) { 1054 /* Can't save anything */ 1055 return; 1056 } 1057 1058 ftr.word = 0; 1059 ftr.s.type = OCS_Q_HIST_TYPE_WQE; 1060 ocs_lock(&q_hist->q_hist_lock); 1061 /* Capture words in reverse order since we'll be interpretting them LIFO */ 1062 for (i = ((sizeof(wqe_word_mask)*8) - 1); i >= 0; i--){ 1063 if ((wqe_word_mask >> i) & 1) { 1064 q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; 1065 q_hist->q_hist_index++; 1066 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1067 } 1068 } 1069 1070 ocs_queue_history_add_q_info(q_hist, qid, qindex); 1071 ocs_queue_history_add_timestamp(q_hist); 1072 1073 /* write footer */ 1074 if (wqe_word_mask) { 1075 ftr.s.mask = wqe_word_mask; 1076 q_hist->q_hist[q_hist->q_hist_index] = ftr.word; 1077 q_hist->q_hist_index++; 1078 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1079 } 1080 1081 ocs_unlock(&q_hist->q_hist_lock); 1082 } 1083 1084 /** 1085 * @ingroup debug 1086 * @brief Log misc words 1087 * 1088 * @param q_hist Pointer to the queue history object. 1089 * @param entryw array of words 1090 * @param num_words number of words in entryw 1091 * 1092 * @return none 1093 */ 1094 void 1095 ocs_queue_history_misc(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t num_words) 1096 { 1097 int i; 1098 ocs_q_hist_ftr_t ftr; 1099 uint32_t mask = 0; 1100 1101 if (q_hist->q_hist == NULL) { 1102 /* Can't save anything */ 1103 return; 1104 } 1105 1106 ftr.word = 0; 1107 ftr.s.type = OCS_Q_HIST_TYPE_MISC; 1108 ocs_lock(&q_hist->q_hist_lock); 1109 /* Capture words in reverse order since we'll be interpretting them LIFO */ 1110 for (i = num_words-1; i >= 0; i--) { 1111 q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; 1112 q_hist->q_hist_index++; 1113 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1114 mask |= BIT(i); 1115 } 1116 1117 ocs_queue_history_add_timestamp(q_hist); 1118 1119 /* write footer */ 1120 if (num_words) { 1121 ftr.s.mask = mask; 1122 q_hist->q_hist[q_hist->q_hist_index] = ftr.word; 1123 q_hist->q_hist_index++; 1124 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1125 } 1126 1127 ocs_unlock(&q_hist->q_hist_lock); 1128 } 1129 1130 /** 1131 * @ingroup debug 1132 * @brief Log work queue completion (CQE) entry into history 1133 * array 1134 * 1135 * @param q_hist Pointer to the queue history object. 1136 * @param ctype Type of completion entry 1137 * @param entryw Completion queue entry in words 1138 * @param status Completion queue status 1139 * @param qid Queue ID 1140 * @param qindex Queue index 1141 * 1142 * @return none 1143 */ 1144 void 1145 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) 1146 { 1147 int i; 1148 unsigned j; 1149 uint32_t cqe_word_mask = 0; 1150 ocs_q_hist_ftr_t ftr; 1151 1152 if (q_hist->q_hist == NULL) { 1153 /* Can't save anything */ 1154 return; 1155 } 1156 1157 ftr.word = 0; 1158 for (j = 0; j < ARRAY_SIZE(ocs_q_hist_cqe_masks); j++) { 1159 if (ocs_q_hist_cqe_masks[j].ctype == ctype) { 1160 ftr.s.type = ocs_q_hist_cqe_masks[j].type; 1161 if (status != 0) { 1162 cqe_word_mask = ocs_q_hist_cqe_masks[j].mask_err; 1163 } else { 1164 cqe_word_mask = ocs_q_hist_cqe_masks[j].mask; 1165 } 1166 } 1167 } 1168 ocs_lock(&q_hist->q_hist_lock); 1169 /* Capture words in reverse order since we'll be interpretting them LIFO */ 1170 for (i = ((sizeof(cqe_word_mask)*8) - 1); i >= 0; i--){ 1171 if ((cqe_word_mask >> i) & 1) { 1172 q_hist->q_hist[q_hist->q_hist_index] = entryw[i]; 1173 q_hist->q_hist_index++; 1174 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1175 } 1176 } 1177 ocs_queue_history_add_q_info(q_hist, qid, qindex); 1178 ocs_queue_history_add_timestamp(q_hist); 1179 1180 /* write footer */ 1181 if (cqe_word_mask) { 1182 ftr.s.mask = cqe_word_mask; 1183 q_hist->q_hist[q_hist->q_hist_index] = ftr.word; 1184 q_hist->q_hist_index++; 1185 q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE; 1186 } 1187 1188 ocs_unlock(&q_hist->q_hist_lock); 1189 } 1190 1191 /** 1192 * @brief Get previous index 1193 * 1194 * @param index Index from which previous index is derived. 1195 */ 1196 uint32_t 1197 ocs_queue_history_prev_index(uint32_t index) 1198 { 1199 if (index == 0) { 1200 return OCS_Q_HIST_SIZE - 1; 1201 } else { 1202 return index - 1; 1203 } 1204 } 1205 1206 #endif /* OCS_DEBUG_QUEUE_HISTORY */ 1207 1208 /** 1209 * @brief Display service parameters 1210 * 1211 * <description> 1212 * 1213 * @param prelabel leading display label 1214 * @param reqlabel display label 1215 * @param dest destination 0=ocs_log, 1=textbuf 1216 * @param textbuf text buffer destination (if dest==1) 1217 * @param sparams pointer to service parameter 1218 * 1219 * @return none 1220 */ 1221 1222 void 1223 ocs_display_sparams(const char *prelabel, const char *reqlabel, int dest, void *textbuf, void *sparams) 1224 { 1225 char label[64]; 1226 1227 if (sparams == NULL) { 1228 return; 1229 } 1230 1231 switch(dest) { 1232 case 0: 1233 if (prelabel != NULL) { 1234 ocs_snprintf(label, sizeof(label), "[%s] sparam: %s", prelabel, reqlabel); 1235 } else { 1236 ocs_snprintf(label, sizeof(label), "sparam: %s", reqlabel); 1237 } 1238 1239 ocs_dump32(OCS_DEBUG_ENABLE_SPARAM_DUMP, NULL, label, sparams, sizeof(fc_plogi_payload_t)); 1240 break; 1241 case 1: 1242 ocs_ddump_buffer((ocs_textbuf_t*) textbuf, reqlabel, 0, sparams, sizeof(fc_plogi_payload_t)); 1243 break; 1244 } 1245 } 1246 1247 /** 1248 * @brief Calculate the T10 PI CRC guard value for a block. 1249 * 1250 * @param buffer Pointer to the data buffer. 1251 * @param size Number of bytes. 1252 * @param crc Previously-calculated CRC, or 0 for a new block. 1253 * 1254 * @return Returns the calculated CRC, which may be passed back in for partial blocks. 1255 * 1256 */ 1257 1258 uint16_t 1259 ocs_scsi_dif_calc_crc(const uint8_t *buffer, uint32_t size, uint16_t crc) 1260 { 1261 return t10crc16(buffer, size, crc); 1262 } 1263 1264 /** 1265 * @brief Calculate the IP-checksum guard value for a block. 1266 * 1267 * @param addrlen array of address length pairs 1268 * @param addrlen_count number of entries in the addrlen[] array 1269 * 1270 * Algorithm: 1271 * Sum all all the 16-byte words in the block 1272 * Add in the "carry", which is everything in excess of 16-bits 1273 * Flip all the bits 1274 * 1275 * @return Returns the calculated checksum 1276 */ 1277 1278 uint16_t 1279 ocs_scsi_dif_calc_checksum(ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count) 1280 { 1281 uint32_t i, j; 1282 uint16_t checksum; 1283 uint32_t intermediate; /* Use an intermediate to hold more than 16 bits during calculations */ 1284 uint32_t count; 1285 uint16_t *buffer; 1286 1287 intermediate = 0; 1288 for (j = 0; j < addrlen_count; j++) { 1289 buffer = addrlen[j].vaddr; 1290 count = addrlen[j].length / 2; 1291 for (i=0; i < count; i++) { 1292 intermediate += buffer[i]; 1293 } 1294 } 1295 1296 /* Carry is everything over 16 bits */ 1297 intermediate += ((intermediate & 0xffff0000) >> 16); 1298 1299 /* Flip all the bits */ 1300 intermediate = ~intermediate; 1301 1302 checksum = intermediate; 1303 1304 return checksum; 1305 } 1306 1307 /** 1308 * @brief Return blocksize given SCSI API DIF block size 1309 * 1310 * Given the DIF block size enumerated value, return the block size value. (e.g. 1311 * OCS_SCSI_DIF_BLK_SIZE_512 returns 512) 1312 * 1313 * @param dif_info Pointer to SCSI API DIF info block 1314 * 1315 * @return returns block size, or 0 if SCSI API DIF blocksize is invalid 1316 */ 1317 1318 uint32_t 1319 ocs_scsi_dif_blocksize(ocs_scsi_dif_info_t *dif_info) 1320 { 1321 uint32_t blocksize = 0; 1322 1323 switch(dif_info->blk_size) { 1324 case OCS_SCSI_DIF_BK_SIZE_512: blocksize = 512; break; 1325 case OCS_SCSI_DIF_BK_SIZE_1024: blocksize = 1024; break; 1326 case OCS_SCSI_DIF_BK_SIZE_2048: blocksize = 2048; break; 1327 case OCS_SCSI_DIF_BK_SIZE_4096: blocksize = 4096; break; 1328 case OCS_SCSI_DIF_BK_SIZE_520: blocksize = 520; break; 1329 case OCS_SCSI_DIF_BK_SIZE_4104: blocksize = 4104; break; 1330 default: 1331 break; 1332 } 1333 1334 return blocksize; 1335 } 1336 1337 /** 1338 * @brief Set SCSI API DIF blocksize 1339 * 1340 * Given a blocksize value (512, 1024, etc.), set the SCSI API DIF blocksize 1341 * in the DIF info block 1342 * 1343 * @param dif_info Pointer to the SCSI API DIF info block 1344 * @param blocksize Block size 1345 * 1346 * @return returns 0 for success, a negative error code value for failure. 1347 */ 1348 1349 int32_t 1350 ocs_scsi_dif_set_blocksize(ocs_scsi_dif_info_t *dif_info, uint32_t blocksize) 1351 { 1352 int32_t rc = 0; 1353 1354 switch(blocksize) { 1355 case 512: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_512; break; 1356 case 1024: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_1024; break; 1357 case 2048: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_2048; break; 1358 case 4096: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4096; break; 1359 case 520: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_520; break; 1360 case 4104: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4104; break; 1361 default: 1362 rc = -1; 1363 break; 1364 } 1365 return rc; 1366 1367 } 1368 1369 /** 1370 * @brief Return memory block size given SCSI DIF API 1371 * 1372 * The blocksize in memory for the DIF transfer is returned, given the SCSI DIF info 1373 * block and the direction of transfer. 1374 * 1375 * @param dif_info Pointer to DIF info block 1376 * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire 1377 * 1378 * @return Memory blocksize, or negative error value 1379 * 1380 * WARNING: the order of initialization of the adj[] arrays MUST match the declarations 1381 * of OCS_SCSI_DIF_OPER_* 1382 */ 1383 1384 int32_t 1385 ocs_scsi_dif_mem_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem) 1386 { 1387 uint32_t blocksize; 1388 uint8_t wiretomem_adj[] = { 1389 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 1390 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 1391 0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 1392 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1393 0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1394 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ 1395 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1396 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1397 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1398 DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ 1399 uint8_t memtowire_adj[] = { 1400 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 1401 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 1402 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 1403 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1404 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1405 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ 1406 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1407 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1408 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1409 DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ 1410 1411 blocksize = ocs_scsi_dif_blocksize(dif_info); 1412 if (blocksize == 0) { 1413 return -1; 1414 } 1415 1416 if (wiretomem) { 1417 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); 1418 blocksize += wiretomem_adj[dif_info->dif_oper]; 1419 } else { /* mem to wire */ 1420 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); 1421 blocksize += memtowire_adj[dif_info->dif_oper]; 1422 } 1423 return blocksize; 1424 } 1425 1426 /** 1427 * @brief Return wire block size given SCSI DIF API 1428 * 1429 * The blocksize on the wire for the DIF transfer is returned, given the SCSI DIF info 1430 * block and the direction of transfer. 1431 * 1432 * @param dif_info Pointer to DIF info block 1433 * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire 1434 * 1435 * @return Wire blocksize or negative error value 1436 * 1437 * WARNING: the order of initialization of the adj[] arrays MUST match the declarations 1438 * of OCS_SCSI_DIF_OPER_* 1439 */ 1440 1441 int32_t 1442 ocs_scsi_dif_wire_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem) 1443 { 1444 uint32_t blocksize; 1445 uint8_t wiretomem_adj[] = { 1446 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 1447 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 1448 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 1449 0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1450 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1451 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ 1452 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1453 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1454 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1455 DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ 1456 uint8_t memtowire_adj[] = { 1457 0, /* OCS_SCSI_DIF_OPER_DISABLED, */ 1458 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */ 1459 0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */ 1460 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1461 0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1462 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */ 1463 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1464 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1465 DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1466 DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */ 1467 1468 1469 blocksize = ocs_scsi_dif_blocksize(dif_info); 1470 if (blocksize == 0) { 1471 return -1; 1472 } 1473 1474 if (wiretomem) { 1475 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); 1476 blocksize += wiretomem_adj[dif_info->dif_oper]; 1477 } else { /* mem to wire */ 1478 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); 1479 blocksize += memtowire_adj[dif_info->dif_oper]; 1480 } 1481 1482 return blocksize; 1483 } 1484 /** 1485 * @brief Return blocksize given HW API DIF block size 1486 * 1487 * Given the DIF block size enumerated value, return the block size value. (e.g. 1488 * OCS_SCSI_DIF_BLK_SIZE_512 returns 512) 1489 * 1490 * @param dif_info Pointer to HW API DIF info block 1491 * 1492 * @return returns block size, or 0 if HW API DIF blocksize is invalid 1493 */ 1494 1495 uint32_t 1496 ocs_hw_dif_blocksize(ocs_hw_dif_info_t *dif_info) 1497 { 1498 uint32_t blocksize = 0; 1499 1500 switch(dif_info->blk_size) { 1501 case OCS_HW_DIF_BK_SIZE_512: blocksize = 512; break; 1502 case OCS_HW_DIF_BK_SIZE_1024: blocksize = 1024; break; 1503 case OCS_HW_DIF_BK_SIZE_2048: blocksize = 2048; break; 1504 case OCS_HW_DIF_BK_SIZE_4096: blocksize = 4096; break; 1505 case OCS_HW_DIF_BK_SIZE_520: blocksize = 520; break; 1506 case OCS_HW_DIF_BK_SIZE_4104: blocksize = 4104; break; 1507 default: 1508 break; 1509 } 1510 1511 return blocksize; 1512 } 1513 1514 /** 1515 * @brief Return memory block size given HW DIF API 1516 * 1517 * The blocksize in memory for the DIF transfer is returned, given the HW DIF info 1518 * block and the direction of transfer. 1519 * 1520 * @param dif_info Pointer to DIF info block 1521 * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire 1522 * 1523 * @return Memory blocksize, or negative error value 1524 * 1525 * WARNING: the order of initialization of the adj[] arrays MUST match the declarations 1526 * of OCS_HW_DIF_OPER_* 1527 */ 1528 1529 int32_t 1530 ocs_hw_dif_mem_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem) 1531 { 1532 uint32_t blocksize; 1533 uint8_t wiretomem_adj[] = { 1534 0, /* OCS_HW_DIF_OPER_DISABLED, */ 1535 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 1536 0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 1537 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1538 0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1539 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ 1540 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1541 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1542 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1543 DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ 1544 uint8_t memtowire_adj[] = { 1545 0, /* OCS_HW_DIF_OPER_DISABLED, */ 1546 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 1547 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 1548 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1549 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1550 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ 1551 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1552 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1553 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1554 DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ 1555 1556 blocksize = ocs_hw_dif_blocksize(dif_info); 1557 if (blocksize == 0) { 1558 return -1; 1559 } 1560 1561 if (wiretomem) { 1562 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); 1563 blocksize += wiretomem_adj[dif_info->dif_oper]; 1564 } else { /* mem to wire */ 1565 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); 1566 blocksize += memtowire_adj[dif_info->dif_oper]; 1567 } 1568 return blocksize; 1569 } 1570 1571 /** 1572 * @brief Return wire block size given HW DIF API 1573 * 1574 * The blocksize on the wire for the DIF transfer is returned, given the HW DIF info 1575 * block and the direction of transfer. 1576 * 1577 * @param dif_info Pointer to DIF info block 1578 * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire 1579 * 1580 * @return Wire blocksize or negative error value 1581 * 1582 * WARNING: the order of initialization of the adj[] arrays MUST match the declarations 1583 * of OCS_HW_DIF_OPER_* 1584 */ 1585 1586 int32_t 1587 ocs_hw_dif_wire_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem) 1588 { 1589 uint32_t blocksize; 1590 uint8_t wiretomem_adj[] = { 1591 0, /* OCS_HW_DIF_OPER_DISABLED, */ 1592 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 1593 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 1594 0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1595 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1596 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ 1597 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1598 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1599 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1600 DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ 1601 uint8_t memtowire_adj[] = { 1602 0, /* OCS_HW_DIF_OPER_DISABLED, */ 1603 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */ 1604 0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */ 1605 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */ 1606 0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */ 1607 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */ 1608 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */ 1609 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */ 1610 DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */ 1611 DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */ 1612 1613 1614 blocksize = ocs_hw_dif_blocksize(dif_info); 1615 if (blocksize == 0) { 1616 return -1; 1617 } 1618 1619 if (wiretomem) { 1620 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0); 1621 blocksize += wiretomem_adj[dif_info->dif_oper]; 1622 } else { /* mem to wire */ 1623 ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0); 1624 blocksize += memtowire_adj[dif_info->dif_oper]; 1625 } 1626 1627 return blocksize; 1628 } 1629 1630 static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment); 1631 static ocs_textbuf_segment_t *ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf); 1632 static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment); 1633 static ocs_textbuf_segment_t *ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx); 1634 1635 uint8_t * 1636 ocs_textbuf_get_buffer(ocs_textbuf_t *textbuf) 1637 { 1638 return ocs_textbuf_ext_get_buffer(textbuf, 0); 1639 } 1640 1641 int32_t 1642 ocs_textbuf_get_length(ocs_textbuf_t *textbuf) 1643 { 1644 return ocs_textbuf_ext_get_length(textbuf, 0); 1645 } 1646 1647 int32_t 1648 ocs_textbuf_get_written(ocs_textbuf_t *textbuf) 1649 { 1650 uint32_t idx; 1651 int32_t n; 1652 int32_t total = 0; 1653 1654 for (idx = 0; (n = ocs_textbuf_ext_get_written(textbuf, idx)) >= 0; idx++) { 1655 total += n; 1656 } 1657 return total; 1658 } 1659 1660 uint8_t *ocs_textbuf_ext_get_buffer(ocs_textbuf_t *textbuf, uint32_t idx) 1661 { 1662 ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); 1663 if (segment == NULL) { 1664 return NULL; 1665 } 1666 return segment->buffer; 1667 } 1668 1669 int32_t ocs_textbuf_ext_get_length(ocs_textbuf_t *textbuf, uint32_t idx) 1670 { 1671 ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); 1672 if (segment == NULL) { 1673 return -1; 1674 } 1675 return segment->buffer_length; 1676 } 1677 1678 int32_t ocs_textbuf_ext_get_written(ocs_textbuf_t *textbuf, uint32_t idx) 1679 { 1680 ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx); 1681 if (segment == NULL) { 1682 return -1; 1683 } 1684 return segment->buffer_written; 1685 } 1686 1687 uint32_t 1688 ocs_textbuf_initialized(ocs_textbuf_t *textbuf) 1689 { 1690 return (textbuf->ocs != NULL); 1691 } 1692 1693 int32_t 1694 ocs_textbuf_alloc(ocs_t *ocs, ocs_textbuf_t *textbuf, uint32_t length) 1695 { 1696 ocs_memset(textbuf, 0, sizeof(*textbuf)); 1697 1698 textbuf->ocs = ocs; 1699 ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link); 1700 1701 if (length > OCS_TEXTBUF_MAX_ALLOC_LEN) { 1702 textbuf->allocation_length = OCS_TEXTBUF_MAX_ALLOC_LEN; 1703 } else { 1704 textbuf->allocation_length = length; 1705 } 1706 1707 /* mark as extendable */ 1708 textbuf->extendable = TRUE; 1709 1710 /* save maximum allocation length */ 1711 textbuf->max_allocation_length = length; 1712 1713 /* Add first segment */ 1714 return (ocs_textbuf_segment_alloc(textbuf) == NULL) ? -1 : 0; 1715 } 1716 1717 static ocs_textbuf_segment_t * 1718 ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf) 1719 { 1720 ocs_textbuf_segment_t *segment = NULL; 1721 1722 if (textbuf->extendable) { 1723 segment = ocs_malloc(textbuf->ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT); 1724 if (segment != NULL) { 1725 segment->buffer = ocs_malloc(textbuf->ocs, textbuf->allocation_length, OCS_M_ZERO | OCS_M_NOWAIT); 1726 if (segment->buffer != NULL) { 1727 segment->buffer_length = textbuf->allocation_length; 1728 segment->buffer_written = 0; 1729 ocs_list_add_tail(&textbuf->segment_list, segment); 1730 textbuf->total_allocation_length += textbuf->allocation_length; 1731 1732 /* If we've allocated our limit, then mark as not extendable */ 1733 if (textbuf->total_allocation_length >= textbuf->max_allocation_length) { 1734 textbuf->extendable = 0; 1735 } 1736 1737 } else { 1738 ocs_textbuf_segment_free(textbuf->ocs, segment); 1739 segment = NULL; 1740 } 1741 } 1742 } 1743 return segment; 1744 } 1745 1746 static void 1747 ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment) 1748 { 1749 if (segment) { 1750 if (segment->buffer && !segment->user_allocated) { 1751 ocs_free(ocs, segment->buffer, segment->buffer_length); 1752 } 1753 ocs_free(ocs, segment, sizeof(*segment)); 1754 } 1755 } 1756 1757 static ocs_textbuf_segment_t * 1758 ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx) 1759 { 1760 uint32_t i; 1761 ocs_textbuf_segment_t *segment; 1762 1763 if (ocs_textbuf_initialized(textbuf)) { 1764 i = 0; 1765 ocs_list_foreach(&textbuf->segment_list, segment) { 1766 if (i == idx) { 1767 return segment; 1768 } 1769 i++; 1770 } 1771 } 1772 return NULL; 1773 } 1774 1775 int32_t 1776 ocs_textbuf_init(ocs_t *ocs, ocs_textbuf_t *textbuf, void *buffer, uint32_t length) 1777 { 1778 int32_t rc = -1; 1779 ocs_textbuf_segment_t *segment; 1780 1781 ocs_memset(textbuf, 0, sizeof(*textbuf)); 1782 1783 textbuf->ocs = ocs; 1784 ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link); 1785 segment = ocs_malloc(ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT); 1786 if (segment) { 1787 segment->buffer = buffer; 1788 segment->buffer_length = length; 1789 segment->buffer_written = 0; 1790 segment->user_allocated = 1; 1791 ocs_list_add_tail(&textbuf->segment_list, segment); 1792 rc = 0; 1793 } 1794 1795 return rc; 1796 } 1797 1798 void 1799 ocs_textbuf_free(ocs_t *ocs, ocs_textbuf_t *textbuf) 1800 { 1801 ocs_textbuf_segment_t *segment; 1802 ocs_textbuf_segment_t *n; 1803 1804 if (ocs_textbuf_initialized(textbuf)) { 1805 ocs_list_foreach_safe(&textbuf->segment_list, segment, n) { 1806 ocs_list_remove(&textbuf->segment_list, segment); 1807 ocs_textbuf_segment_free(ocs, segment); 1808 } 1809 1810 ocs_memset(textbuf, 0, sizeof(*textbuf)); 1811 } 1812 } 1813 1814 void 1815 ocs_textbuf_printf(ocs_textbuf_t *textbuf, const char *fmt, ...) 1816 { 1817 va_list ap; 1818 1819 if (ocs_textbuf_initialized(textbuf)) { 1820 va_start(ap, fmt); 1821 ocs_textbuf_vprintf(textbuf, fmt, ap); 1822 va_end(ap); 1823 } 1824 } 1825 1826 void 1827 ocs_textbuf_vprintf(ocs_textbuf_t *textbuf, const char *fmt, va_list ap) 1828 { 1829 int avail; 1830 int written; 1831 ocs_textbuf_segment_t *segment; 1832 va_list save_ap; 1833 1834 if (!ocs_textbuf_initialized(textbuf)) { 1835 return; 1836 } 1837 1838 va_copy(save_ap, ap); 1839 1840 /* fetch last segment */ 1841 segment = ocs_list_get_tail(&textbuf->segment_list); 1842 1843 avail = ocs_segment_remaining(segment); 1844 if (avail == 0) { 1845 if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) { 1846 goto out; 1847 } 1848 avail = ocs_segment_remaining(segment); 1849 } 1850 1851 written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, ap); 1852 1853 /* See if data was truncated */ 1854 if (written >= avail) { 1855 1856 written = avail; 1857 1858 if (textbuf->extendable) { 1859 1860 /* revert the partially written data */ 1861 *(segment->buffer + segment->buffer_written) = 0; 1862 1863 /* Allocate a new segment */ 1864 if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) { 1865 ocs_log_err(textbuf->ocs, "alloc segment failed\n"); 1866 goto out; 1867 } 1868 avail = ocs_segment_remaining(segment); 1869 1870 /* Retry the write */ 1871 written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, save_ap); 1872 } 1873 } 1874 segment->buffer_written += written; 1875 1876 out: 1877 va_end(save_ap); 1878 } 1879 1880 void 1881 ocs_textbuf_putc(ocs_textbuf_t *textbuf, uint8_t c) 1882 { 1883 ocs_textbuf_segment_t *segment; 1884 1885 if (ocs_textbuf_initialized(textbuf)) { 1886 segment = ocs_list_get_tail(&textbuf->segment_list); 1887 1888 if (ocs_segment_remaining(segment)) { 1889 *(segment->buffer + segment->buffer_written++) = c; 1890 } 1891 if (ocs_segment_remaining(segment) == 0) { 1892 ocs_textbuf_segment_alloc(textbuf); 1893 } 1894 } 1895 } 1896 1897 void 1898 ocs_textbuf_puts(ocs_textbuf_t *textbuf, char *s) 1899 { 1900 if (ocs_textbuf_initialized(textbuf)) { 1901 while(*s) { 1902 ocs_textbuf_putc(textbuf, *s++); 1903 } 1904 } 1905 } 1906 1907 void 1908 ocs_textbuf_buffer(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length) 1909 { 1910 char *s; 1911 1912 if (!ocs_textbuf_initialized(textbuf)) { 1913 return; 1914 } 1915 1916 s = (char*) buffer; 1917 while(*s) { 1918 1919 /* 1920 * XML escapes 1921 * 1922 * " " 1923 * ' ' 1924 * < < 1925 * > > 1926 * & & 1927 */ 1928 1929 switch(*s) { 1930 case '"': ocs_textbuf_puts(textbuf, """); break; 1931 case '\'': ocs_textbuf_puts(textbuf, "'"); break; 1932 case '<': ocs_textbuf_puts(textbuf, "<"); break; 1933 case '>': ocs_textbuf_puts(textbuf, ">"); break; 1934 case '&': ocs_textbuf_puts(textbuf, "&"); break; 1935 default: ocs_textbuf_putc(textbuf, *s); break; 1936 } 1937 s++; 1938 } 1939 1940 } 1941 1942 void 1943 ocs_textbuf_copy(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length) 1944 { 1945 char *s; 1946 1947 if (!ocs_textbuf_initialized(textbuf)) { 1948 return; 1949 } 1950 1951 s = (char*) buffer; 1952 while(*s) { 1953 ocs_textbuf_putc(textbuf, *s++); 1954 } 1955 1956 } 1957 1958 int32_t 1959 ocs_textbuf_remaining(ocs_textbuf_t *textbuf) 1960 { 1961 if (ocs_textbuf_initialized(textbuf)) { 1962 return ocs_segment_remaining(ocs_list_get_head(&textbuf->segment_list)); 1963 } else { 1964 return 0; 1965 } 1966 } 1967 1968 static int32_t 1969 ocs_segment_remaining(ocs_textbuf_segment_t *segment) 1970 { 1971 return segment->buffer_length - segment->buffer_written; 1972 } 1973 1974 void 1975 ocs_textbuf_reset(ocs_textbuf_t *textbuf) 1976 { 1977 uint32_t i = 0; 1978 ocs_textbuf_segment_t *segment; 1979 ocs_textbuf_segment_t *n; 1980 1981 if (ocs_textbuf_initialized(textbuf)) { 1982 /* zero written on the first segment, free the rest */ 1983 ocs_list_foreach_safe(&textbuf->segment_list, segment, n) { 1984 if (i++ == 0) { 1985 segment->buffer_written = 0; 1986 } else { 1987 ocs_list_remove(&textbuf->segment_list, segment); 1988 ocs_textbuf_segment_free(textbuf->ocs, segment); 1989 } 1990 } 1991 } 1992 } 1993 1994 /** 1995 * @brief Sparse Vector API. 1996 * 1997 * This is a trimmed down sparse vector implementation tuned to the problem of 1998 * 24-bit FC_IDs. In this case, the 24-bit index value is broken down in three 1999 * 8-bit values. These values are used to index up to three 256 element arrays. 2000 * Arrays are allocated, only when needed. @n @n 2001 * The lookup can complete in constant time (3 indexed array references). @n @n 2002 * A typical use case would be that the fabric/directory FC_IDs would cause two rows to be 2003 * allocated, and the fabric assigned remote nodes would cause two rows to be allocated, with 2004 * the root row always allocated. This gives five rows of 256 x sizeof(void*), 2005 * resulting in 10k. 2006 */ 2007 2008 2009 2010 /** 2011 * @ingroup spv 2012 * @brief Allocate a new sparse vector row. 2013 * 2014 * @param os OS handle 2015 * @param rowcount Count of rows. 2016 * 2017 * @par Description 2018 * A new sparse vector row is allocated. 2019 * 2020 * @param rowcount Number of elements in a row. 2021 * 2022 * @return Returns the pointer to a row. 2023 */ 2024 static void 2025 **spv_new_row(ocs_os_handle_t os, uint32_t rowcount) 2026 { 2027 return ocs_malloc(os, sizeof(void*) * rowcount, OCS_M_ZERO | OCS_M_NOWAIT); 2028 } 2029 2030 2031 2032 /** 2033 * @ingroup spv 2034 * @brief Delete row recursively. 2035 * 2036 * @par Description 2037 * This function recursively deletes the rows in this sparse vector 2038 * 2039 * @param os OS handle 2040 * @param a Pointer to the row. 2041 * @param n Number of elements in the row. 2042 * @param depth Depth of deleting. 2043 * 2044 * @return None. 2045 */ 2046 static void 2047 _spv_del(ocs_os_handle_t os, void **a, uint32_t n, uint32_t depth) 2048 { 2049 if (a) { 2050 if (depth) { 2051 uint32_t i; 2052 2053 for (i = 0; i < n; i ++) { 2054 _spv_del(os, a[i], n, depth-1); 2055 } 2056 2057 ocs_free(os, a, SPV_ROWLEN*sizeof(*a)); 2058 } 2059 } 2060 } 2061 2062 /** 2063 * @ingroup spv 2064 * @brief Delete a sparse vector. 2065 * 2066 * @par Description 2067 * The sparse vector is freed. 2068 * 2069 * @param spv Pointer to the sparse vector object. 2070 */ 2071 void 2072 spv_del(sparse_vector_t spv) 2073 { 2074 if (spv) { 2075 _spv_del(spv->os, spv->array, SPV_ROWLEN, SPV_DIM); 2076 ocs_free(spv->os, spv, sizeof(*spv)); 2077 } 2078 } 2079 2080 /** 2081 * @ingroup spv 2082 * @brief Instantiate a new sparse vector object. 2083 * 2084 * @par Description 2085 * A new sparse vector is allocated. 2086 * 2087 * @param os OS handle 2088 * 2089 * @return Returns the pointer to the sparse vector, or NULL. 2090 */ 2091 sparse_vector_t 2092 spv_new(ocs_os_handle_t os) 2093 { 2094 sparse_vector_t spv; 2095 uint32_t i; 2096 2097 spv = ocs_malloc(os, sizeof(*spv), OCS_M_ZERO | OCS_M_NOWAIT); 2098 if (!spv) { 2099 return NULL; 2100 } 2101 2102 spv->os = os; 2103 spv->max_idx = 1; 2104 for (i = 0; i < SPV_DIM; i ++) { 2105 spv->max_idx *= SPV_ROWLEN; 2106 } 2107 2108 return spv; 2109 } 2110 2111 /** 2112 * @ingroup spv 2113 * @brief Return the address of a cell. 2114 * 2115 * @par Description 2116 * Returns the address of a cell, allocates sparse rows as needed if the 2117 * alloc_new_rows parameter is set. 2118 * 2119 * @param sv Pointer to the sparse vector. 2120 * @param idx Index of which to return the address. 2121 * @param alloc_new_rows If TRUE, then new rows may be allocated to set values, 2122 * Set to FALSE for retrieving values. 2123 * 2124 * @return Returns the pointer to the cell, or NULL. 2125 */ 2126 static void 2127 *spv_new_cell(sparse_vector_t sv, uint32_t idx, uint8_t alloc_new_rows) 2128 { 2129 uint32_t a = (idx >> 16) & 0xff; 2130 uint32_t b = (idx >> 8) & 0xff; 2131 uint32_t c = (idx >> 0) & 0xff; 2132 void **p; 2133 2134 if (idx >= sv->max_idx) { 2135 return NULL; 2136 } 2137 2138 if (sv->array == NULL) { 2139 sv->array = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); 2140 if (sv->array == NULL) { 2141 return NULL; 2142 } 2143 } 2144 p = sv->array; 2145 if (p[a] == NULL) { 2146 p[a] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); 2147 if (p[a] == NULL) { 2148 return NULL; 2149 } 2150 } 2151 p = p[a]; 2152 if (p[b] == NULL) { 2153 p[b] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL); 2154 if (p[b] == NULL) { 2155 return NULL; 2156 } 2157 } 2158 p = p[b]; 2159 2160 return &p[c]; 2161 } 2162 2163 /** 2164 * @ingroup spv 2165 * @brief Set the sparse vector cell value. 2166 * 2167 * @par Description 2168 * Sets the sparse vector at @c idx to @c value. 2169 * 2170 * @param sv Pointer to the sparse vector. 2171 * @param idx Index of which to store. 2172 * @param value Value to store. 2173 * 2174 * @return None. 2175 */ 2176 void 2177 spv_set(sparse_vector_t sv, uint32_t idx, void *value) 2178 { 2179 void **ref = spv_new_cell(sv, idx, TRUE); 2180 if (ref) { 2181 *ref = value; 2182 } 2183 } 2184 2185 /** 2186 * @ingroup spv 2187 * @brief Return the sparse vector cell value. 2188 * 2189 * @par Description 2190 * Returns the value at @c idx. 2191 * 2192 * @param sv Pointer to the sparse vector. 2193 * @param idx Index of which to return the value. 2194 * 2195 * @return Returns the cell value, or NULL. 2196 */ 2197 void 2198 *spv_get(sparse_vector_t sv, uint32_t idx) 2199 { 2200 void **ref = spv_new_cell(sv, idx, FALSE); 2201 if (ref) { 2202 return *ref; 2203 } 2204 return NULL; 2205 } 2206 2207 /*****************************************************************/ 2208 /* */ 2209 /* CRC LOOKUP TABLE */ 2210 /* ================ */ 2211 /* The following CRC lookup table was generated automagically */ 2212 /* by the Rocksoft^tm Model CRC Algorithm Table Generation */ 2213 /* Program V1.0 using the following model parameters: */ 2214 /* */ 2215 /* Width : 2 bytes. */ 2216 /* Poly : 0x8BB7 */ 2217 /* Reverse : FALSE. */ 2218 /* */ 2219 /* For more information on the Rocksoft^tm Model CRC Algorithm, */ 2220 /* see the document titled "A Painless Guide to CRC Error */ 2221 /* Detection Algorithms" by Ross Williams */ 2222 /* (ross@guest.adelaide.edu.au.). This document is likely to be */ 2223 /* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */ 2224 /* */ 2225 /*****************************************************************/ 2226 /* 2227 * Emulex Inc, changes: 2228 * - minor syntax changes for successful compilation with contemporary 2229 * C compilers, and OCS SDK API 2230 * - crctable[] generated using Rocksoft public domain code 2231 * 2232 * Used in the Emulex SDK, the generated file crctable.out is cut and pasted into 2233 * applicable SDK sources. 2234 */ 2235 2236 2237 static unsigned short crctable[256] = 2238 { 2239 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, 2240 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, 2241 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, 2242 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, 2243 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, 2244 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, 2245 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, 2246 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, 2247 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, 2248 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, 2249 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, 2250 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, 2251 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, 2252 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, 2253 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, 2254 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, 2255 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, 2256 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, 2257 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, 2258 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, 2259 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, 2260 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, 2261 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, 2262 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, 2263 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, 2264 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, 2265 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, 2266 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, 2267 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, 2268 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, 2269 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, 2270 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 2271 }; 2272 2273 /*****************************************************************/ 2274 /* End of CRC Lookup Table */ 2275 /*****************************************************************/ 2276 2277 /** 2278 * @brief Calculate the T10 PI CRC guard value for a block. 2279 * 2280 * Code based on Rocksoft's public domain CRC code, refer to 2281 * http://www.ross.net/crc/download/crc_v3.txt. Minimally altered 2282 * to work with the ocs_dif API. 2283 * 2284 * @param blk_adr Pointer to the data buffer. 2285 * @param blk_len Number of bytes. 2286 * @param crc Previously-calculated CRC, or crcseed for a new block. 2287 * 2288 * @return Returns the calculated CRC, which may be passed back in for partial blocks. 2289 * 2290 */ 2291 2292 unsigned short 2293 t10crc16(const unsigned char *blk_adr, unsigned long blk_len, unsigned short crc) 2294 { 2295 if (blk_len > 0) { 2296 while (blk_len--) { 2297 crc = crctable[((crc>>8) ^ *blk_adr++) & 0xFFL] ^ (crc << 8); 2298 } 2299 } 2300 return crc; 2301 } 2302 2303 struct ocs_ramlog_s { 2304 uint32_t initialized; 2305 uint32_t textbuf_count; 2306 uint32_t textbuf_base; 2307 ocs_textbuf_t *textbufs; 2308 uint32_t cur_textbuf_idx; 2309 ocs_textbuf_t *cur_textbuf; 2310 ocs_lock_t lock; 2311 }; 2312 2313 static uint32_t ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx); 2314 2315 /** 2316 * @brief Allocate a ramlog buffer. 2317 * 2318 * Initialize a RAM logging buffer with text buffers totalling buffer_len. 2319 * 2320 * @param ocs Pointer to driver structure. 2321 * @param buffer_len Total length of RAM log buffers. 2322 * @param buffer_count Number of text buffers to allocate (totalling buffer-len). 2323 * 2324 * @return Returns pointer to ocs_ramlog_t instance, or NULL. 2325 */ 2326 ocs_ramlog_t * 2327 ocs_ramlog_init(ocs_t *ocs, uint32_t buffer_len, uint32_t buffer_count) 2328 { 2329 uint32_t i; 2330 uint32_t rc; 2331 ocs_ramlog_t *ramlog; 2332 2333 ramlog = ocs_malloc(ocs, sizeof(*ramlog), OCS_M_ZERO | OCS_M_NOWAIT); 2334 if (ramlog == NULL) { 2335 ocs_log_err(ocs, "ocs_malloc ramlog failed\n"); 2336 return NULL; 2337 } 2338 2339 ramlog->textbuf_count = buffer_count; 2340 2341 ramlog->textbufs = ocs_malloc(ocs, sizeof(*ramlog->textbufs)*buffer_count, OCS_M_ZERO | OCS_M_NOWAIT); 2342 if (ramlog->textbufs == NULL) { 2343 ocs_log_err(ocs, "ocs_malloc textbufs failed\n"); 2344 ocs_ramlog_free(ocs, ramlog); 2345 return NULL; 2346 } 2347 2348 for (i = 0; i < buffer_count; i ++) { 2349 rc = ocs_textbuf_alloc(ocs, &ramlog->textbufs[i], buffer_len); 2350 if (rc) { 2351 ocs_log_err(ocs, "ocs_textbuf_alloc failed\n"); 2352 ocs_ramlog_free(ocs, ramlog); 2353 return NULL; 2354 } 2355 } 2356 2357 ramlog->cur_textbuf_idx = 0; 2358 ramlog->textbuf_base = 1; 2359 ramlog->cur_textbuf = &ramlog->textbufs[0]; 2360 ramlog->initialized = TRUE; 2361 ocs_lock_init(ocs, &ramlog->lock, "ramlog_lock[%d]", ocs_instance(ocs)); 2362 return ramlog; 2363 } 2364 2365 /** 2366 * @brief Free a ramlog buffer. 2367 * 2368 * A previously allocated RAM logging buffer is freed. 2369 * 2370 * @param ocs Pointer to driver structure. 2371 * @param ramlog Pointer to RAM logging buffer structure. 2372 * 2373 * @return None. 2374 */ 2375 2376 void 2377 ocs_ramlog_free(ocs_t *ocs, ocs_ramlog_t *ramlog) 2378 { 2379 uint32_t i; 2380 2381 if (ramlog != NULL) { 2382 ocs_lock_free(&ramlog->lock); 2383 if (ramlog->textbufs) { 2384 for (i = 0; i < ramlog->textbuf_count; i ++) { 2385 ocs_textbuf_free(ocs, &ramlog->textbufs[i]); 2386 } 2387 2388 ocs_free(ocs, ramlog->textbufs, ramlog->textbuf_count*sizeof(*ramlog->textbufs)); 2389 ramlog->textbufs = NULL; 2390 } 2391 ocs_free(ocs, ramlog, sizeof(*ramlog)); 2392 } 2393 } 2394 2395 /** 2396 * @brief Clear a ramlog buffer. 2397 * 2398 * The text in the start of day and/or recent ramlog text buffers is cleared. 2399 * 2400 * @param ocs Pointer to driver structure. 2401 * @param ramlog Pointer to RAM logging buffer structure. 2402 * @param clear_start_of_day Clear the start of day (driver init) portion of the ramlog. 2403 * @param clear_recent Clear the recent messages portion of the ramlog. 2404 * 2405 * @return None. 2406 */ 2407 2408 void 2409 ocs_ramlog_clear(ocs_t *ocs, ocs_ramlog_t *ramlog, int clear_start_of_day, int clear_recent) 2410 { 2411 uint32_t i; 2412 2413 if (clear_recent) { 2414 for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) { 2415 ocs_textbuf_reset(&ramlog->textbufs[i]); 2416 } 2417 ramlog->cur_textbuf_idx = 1; 2418 } 2419 if (clear_start_of_day && ramlog->textbuf_base) { 2420 ocs_textbuf_reset(&ramlog->textbufs[0]); 2421 /* Set textbuf_base to 0, so that all buffers are available for 2422 * recent logs 2423 */ 2424 ramlog->textbuf_base = 0; 2425 } 2426 } 2427 2428 /** 2429 * @brief Append formatted printf data to a ramlog buffer. 2430 * 2431 * Formatted data is appended to a RAM logging buffer. 2432 * 2433 * @param os Pointer to driver structure. 2434 * @param fmt Pointer to printf style format specifier. 2435 * 2436 * @return Returns 0 on success, or a negative error code value on failure. 2437 */ 2438 2439 int32_t 2440 ocs_ramlog_printf(void *os, const char *fmt, ...) 2441 { 2442 ocs_t *ocs = os; 2443 va_list ap; 2444 int32_t res; 2445 2446 if (ocs == NULL || ocs->ramlog == NULL) { 2447 return -1; 2448 } 2449 2450 va_start(ap, fmt); 2451 res = ocs_ramlog_vprintf(ocs->ramlog, fmt, ap); 2452 va_end(ap); 2453 2454 return res; 2455 } 2456 2457 /** 2458 * @brief Append formatted text to a ramlog using variable arguments. 2459 * 2460 * Formatted data is appended to the RAM logging buffer, using variable arguments. 2461 * 2462 * @param ramlog Pointer to RAM logging buffer. 2463 * @param fmt Pointer to printf style formatting string. 2464 * @param ap Variable argument pointer. 2465 * 2466 * @return Returns 0 on success, or a negative error code value on failure. 2467 */ 2468 2469 int32_t 2470 ocs_ramlog_vprintf(ocs_ramlog_t *ramlog, const char *fmt, va_list ap) 2471 { 2472 if (ramlog == NULL || !ramlog->initialized) { 2473 return -1; 2474 } 2475 2476 /* check the current text buffer, if it is almost full (less than 120 characaters), then 2477 * roll to the next one. 2478 */ 2479 ocs_lock(&ramlog->lock); 2480 if (ocs_textbuf_remaining(ramlog->cur_textbuf) < 120) { 2481 ramlog->cur_textbuf_idx = ocs_ramlog_next_idx(ramlog, ramlog->cur_textbuf_idx); 2482 ramlog->cur_textbuf = &ramlog->textbufs[ramlog->cur_textbuf_idx]; 2483 ocs_textbuf_reset(ramlog->cur_textbuf); 2484 } 2485 2486 ocs_textbuf_vprintf(ramlog->cur_textbuf, fmt, ap); 2487 ocs_unlock(&ramlog->lock); 2488 2489 return 0; 2490 } 2491 2492 /** 2493 * @brief Return next ramlog buffer index. 2494 * 2495 * Given a RAM logging buffer index, return the next index. 2496 * 2497 * @param ramlog Pointer to RAM logging buffer. 2498 * @param idx Index value. 2499 * 2500 * @return Returns next index value. 2501 */ 2502 2503 static uint32_t 2504 ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx) 2505 { 2506 idx = idx + 1; 2507 2508 if (idx >= ramlog->textbuf_count) { 2509 idx = ramlog->textbuf_base; 2510 } 2511 2512 return idx; 2513 } 2514 2515 /** 2516 * @brief Perform ramlog buffer driver dump. 2517 * 2518 * The RAM logging buffer is appended to the driver dump data. 2519 * 2520 * @param textbuf Pointer to the driver dump text buffer. 2521 * @param ramlog Pointer to the RAM logging buffer. 2522 * 2523 * @return Returns 0 on success, or a negative error code value on failure. 2524 */ 2525 2526 int32_t 2527 ocs_ddump_ramlog(ocs_textbuf_t *textbuf, ocs_ramlog_t *ramlog) 2528 { 2529 uint32_t i; 2530 ocs_textbuf_t *rltextbuf; 2531 int idx; 2532 2533 if ((ramlog == NULL) || (ramlog->textbufs == NULL)) { 2534 return -1; 2535 } 2536 2537 ocs_ddump_section(textbuf, "driver-log", 0); 2538 2539 /* Dump the start of day buffer */ 2540 ocs_ddump_section(textbuf, "startofday", 0); 2541 /* If textbuf_base is 0, then all buffers are used for recent */ 2542 if (ramlog->textbuf_base) { 2543 rltextbuf = &ramlog->textbufs[0]; 2544 ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf)); 2545 } 2546 ocs_ddump_endsection(textbuf, "startofday", 0); 2547 2548 /* Dump the most recent buffers */ 2549 ocs_ddump_section(textbuf, "recent", 0); 2550 2551 /* start with the next textbuf */ 2552 idx = ocs_ramlog_next_idx(ramlog, ramlog->textbuf_count); 2553 2554 for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) { 2555 rltextbuf = &ramlog->textbufs[idx]; 2556 ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf)); 2557 idx = ocs_ramlog_next_idx(ramlog, idx); 2558 } 2559 ocs_ddump_endsection(textbuf, "recent", 0); 2560 ocs_ddump_endsection(textbuf, "driver-log", 0); 2561 2562 return 0; 2563 } 2564 2565 struct ocs_pool_s { 2566 ocs_os_handle_t os; 2567 ocs_array_t *a; 2568 ocs_list_t freelist; 2569 uint32_t use_lock:1; 2570 ocs_lock_t lock; 2571 }; 2572 2573 typedef struct { 2574 ocs_list_link_t link; 2575 } pool_hdr_t; 2576 2577 2578 /** 2579 * @brief Allocate a memory pool. 2580 * 2581 * A memory pool of given size and item count is allocated. 2582 * 2583 * @param os OS handle. 2584 * @param size Size in bytes of item. 2585 * @param count Number of items in a memory pool. 2586 * @param use_lock TRUE to enable locking of pool. 2587 * 2588 * @return Returns pointer to allocated memory pool, or NULL. 2589 */ 2590 ocs_pool_t * 2591 ocs_pool_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count, uint32_t use_lock) 2592 { 2593 ocs_pool_t *pool; 2594 uint32_t i; 2595 2596 pool = ocs_malloc(os, sizeof(*pool), OCS_M_ZERO | OCS_M_NOWAIT); 2597 if (pool == NULL) { 2598 return NULL; 2599 } 2600 2601 pool->os = os; 2602 pool->use_lock = use_lock; 2603 2604 /* Allocate an array where each array item is the size of a pool_hdr_t plus 2605 * the requested memory item size (size) 2606 */ 2607 pool->a = ocs_array_alloc(os, size + sizeof(pool_hdr_t), count); 2608 if (pool->a == NULL) { 2609 ocs_pool_free(pool); 2610 return NULL; 2611 } 2612 2613 ocs_list_init(&pool->freelist, pool_hdr_t, link); 2614 for (i = 0; i < count; i++) { 2615 ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i)); 2616 } 2617 2618 if (pool->use_lock) { 2619 ocs_lock_init(os, &pool->lock, "ocs_pool:%p", pool); 2620 } 2621 2622 return pool; 2623 } 2624 2625 /** 2626 * @brief Reset a memory pool. 2627 * 2628 * Place all pool elements on the free list, and zero them. 2629 * 2630 * @param pool Pointer to the pool object. 2631 * 2632 * @return None. 2633 */ 2634 void 2635 ocs_pool_reset(ocs_pool_t *pool) 2636 { 2637 uint32_t i; 2638 uint32_t count = ocs_array_get_count(pool->a); 2639 uint32_t size = ocs_array_get_size(pool->a); 2640 2641 if (pool->use_lock) { 2642 ocs_lock(&pool->lock); 2643 } 2644 2645 /* 2646 * Remove all the entries from the free list, otherwise we will 2647 * encountered linked list asserts when they are re-added. 2648 */ 2649 while (!ocs_list_empty(&pool->freelist)) { 2650 ocs_list_remove_head(&pool->freelist); 2651 } 2652 2653 /* Reset the free list */ 2654 ocs_list_init(&pool->freelist, pool_hdr_t, link); 2655 2656 /* Return all elements to the free list and zero the elements */ 2657 for (i = 0; i < count; i++) { 2658 ocs_memset(ocs_pool_get_instance(pool, i), 0, size - sizeof(pool_hdr_t)); 2659 ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i)); 2660 } 2661 if (pool->use_lock) { 2662 ocs_unlock(&pool->lock); 2663 } 2664 2665 } 2666 2667 /** 2668 * @brief Free a previously allocated memory pool. 2669 * 2670 * The memory pool is freed. 2671 * 2672 * @param pool Pointer to memory pool. 2673 * 2674 * @return None. 2675 */ 2676 void 2677 ocs_pool_free(ocs_pool_t *pool) 2678 { 2679 if (pool != NULL) { 2680 if (pool->a != NULL) { 2681 ocs_array_free(pool->a); 2682 } 2683 if (pool->use_lock) { 2684 ocs_lock_free(&pool->lock); 2685 } 2686 ocs_free(pool->os, pool, sizeof(*pool)); 2687 } 2688 } 2689 2690 /** 2691 * @brief Allocate a memory pool item 2692 * 2693 * A memory pool item is taken from the free list and returned. 2694 * 2695 * @param pool Pointer to memory pool. 2696 * 2697 * @return Pointer to allocated item, otherwise NULL if there are no unallocated 2698 * items. 2699 */ 2700 void * 2701 ocs_pool_get(ocs_pool_t *pool) 2702 { 2703 pool_hdr_t *h; 2704 void *item = NULL; 2705 2706 if (pool->use_lock) { 2707 ocs_lock(&pool->lock); 2708 } 2709 2710 h = ocs_list_remove_head(&pool->freelist); 2711 2712 if (h != NULL) { 2713 /* Return the array item address offset by the size of pool_hdr_t */ 2714 item = &h[1]; 2715 } 2716 2717 if (pool->use_lock) { 2718 ocs_unlock(&pool->lock); 2719 } 2720 return item; 2721 } 2722 2723 /** 2724 * @brief free memory pool item 2725 * 2726 * A memory pool item is freed. 2727 * 2728 * @param pool Pointer to memory pool. 2729 * @param item Pointer to item to free. 2730 * 2731 * @return None. 2732 */ 2733 void 2734 ocs_pool_put(ocs_pool_t *pool, void *item) 2735 { 2736 pool_hdr_t *h; 2737 2738 if (pool->use_lock) { 2739 ocs_lock(&pool->lock); 2740 } 2741 2742 /* Fetch the address of the array item, which is the item address negatively offset 2743 * by size of pool_hdr_t (note the index of [-1] 2744 */ 2745 h = &((pool_hdr_t*)item)[-1]; 2746 2747 ocs_list_add_tail(&pool->freelist, h); 2748 2749 if (pool->use_lock) { 2750 ocs_unlock(&pool->lock); 2751 } 2752 2753 } 2754 2755 /** 2756 * @brief Return memory pool item count. 2757 * 2758 * Returns the allocated number of items. 2759 * 2760 * @param pool Pointer to memory pool. 2761 * 2762 * @return Returns count of allocated items. 2763 */ 2764 uint32_t 2765 ocs_pool_get_count(ocs_pool_t *pool) 2766 { 2767 uint32_t count; 2768 if (pool->use_lock) { 2769 ocs_lock(&pool->lock); 2770 } 2771 count = ocs_array_get_count(pool->a); 2772 if (pool->use_lock) { 2773 ocs_unlock(&pool->lock); 2774 } 2775 return count; 2776 } 2777 2778 /** 2779 * @brief Return item given an index. 2780 * 2781 * A pointer to a memory pool item is returned given an index. 2782 * 2783 * @param pool Pointer to memory pool. 2784 * @param idx Index. 2785 * 2786 * @return Returns pointer to item, or NULL if index is invalid. 2787 */ 2788 void * 2789 ocs_pool_get_instance(ocs_pool_t *pool, uint32_t idx) 2790 { 2791 pool_hdr_t *h = ocs_array_get(pool->a, idx); 2792 2793 if (h == NULL) { 2794 return NULL; 2795 } 2796 return &h[1]; 2797 } 2798 2799 /** 2800 * @brief Return count of free objects in a pool. 2801 * 2802 * The number of objects on a pool's free list. 2803 * 2804 * @param pool Pointer to memory pool. 2805 * 2806 * @return Returns count of objects on free list. 2807 */ 2808 uint32_t 2809 ocs_pool_get_freelist_count(ocs_pool_t *pool) 2810 { 2811 uint32_t count = 0; 2812 void *item; 2813 2814 if (pool->use_lock) { 2815 ocs_lock(&pool->lock); 2816 } 2817 2818 ocs_list_foreach(&pool->freelist, item) { 2819 count++; 2820 } 2821 2822 if (pool->use_lock) { 2823 ocs_unlock(&pool->lock); 2824 } 2825 return count; 2826 } 2827