1/***********************license start*************** 2 * Copyright (c) 2003-2010 Cavium Inc. (support@cavium.com). All rights 3 * reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above 14 * copyright notice, this list of conditions and the following 15 * disclaimer in the documentation and/or other materials provided 16 * with the distribution. 17 18 * * Neither the name of Cavium Inc. nor the names of 19 * its contributors may be used to endorse or promote products 20 * derived from this software without specific prior written 21 * permission. 22 23 * This Software, including technical data, may be subject to U.S. export control 24 * laws, including the U.S. Export Administration Act and its associated 25 * regulations, and may be subject to export or import regulations in other 26 * countries. 27 28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" 29 * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR 30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO 31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR 32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM 33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, 34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF 35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR 36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR 37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. 38 ***********************license end**************************************/ 39 40 41 42/** 43 * @file 44 * cvmx-shmem supplies the cross application shared memory implementation 45 * 46 * <hr>$Revision: 41586 $<hr> 47 */ 48#include "cvmx.h" 49#include "cvmx-bootmem.h" 50#include "cvmx-tlb.h" 51#include "cvmx-shmem.h" 52 53//#define DEBUG 54 55struct cvmx_shmem_smdr *__smdr = NULL; 56 57#ifdef CVMX_BUILD_FOR_LINUX_USER 58static int __cvmx_shmem_devmemfd = 0; /* fd for /dev/mem */ 59#endif 60 61#define __CHECK_APP_SMDR do { \ 62 if (__smdr == NULL) { \ 63 cvmx_dprintf("cvmx_shmem: %s is not set up, Quit line %d \n", \ 64 CVMX_SHMEM_DSCPTR_NAME, __LINE__ ); \ 65 exit(-1); \ 66 } \ 67 }while(0) 68 69 70 71/** 72 * @INTERNAL 73 * Virtual sbrk, assigning virtual address in a global virtual address space. 74 * 75 * @param alignment alignment requirement in bytes 76 * @param size size in bytes 77 */ 78static inline void *__cvmx_shmem_vsbrk_64(uint64_t alignment, uint64_t size) 79{ 80 uint64_t nbase_64 = CAST64(__smdr->break64); 81 void *nbase = NULL; 82 83 /* Skip unaligned bytes */ 84 if (nbase_64 & alignment) 85 nbase_64 += ~(nbase_64 & alignment) + 1; 86 87 if (nbase_64 + size < CVMX_SHMEM_VADDR64_END) 88 { 89 nbase = CASTPTR(void *, nbase_64); 90 __smdr->break64 = nbase + size; 91 } 92 93 return nbase; 94} 95 96/** 97 * @INTERNAL 98 * Initialize all SMDR entries, only need to be called once 99 * 100 * @param smdr pointer to the SMDR 101 */ 102static inline void __smdr_new(struct cvmx_shmem_smdr *smdr) { 103 104 if (smdr != NULL) 105 { 106 int i; 107 108 cvmx_spinlock_init (&smdr->lock); 109 cvmx_spinlock_lock (&smdr->lock); 110 111 for ( i = 0; i < CVMX_SHMEM_NUM_DSCPTR; i++ ) 112 { 113 smdr -> shmd[i].owner = CVMX_SHMEM_OWNER_NONE; 114 smdr -> shmd[i].is_named_block = 0; 115 smdr -> shmd[i].use_count = 0; 116 smdr -> shmd[i].name = NULL; 117 smdr -> shmd[i].vaddr = NULL; 118 smdr -> shmd[i].paddr = 0; 119 smdr -> shmd[i].size = 0; 120 smdr -> shmd[i].alignment = 0; 121 }; 122 123 /* Init vaddr */ 124 smdr->break64 = (void *)CVMX_SHMEM_VADDR64_START; 125 cvmx_spinlock_unlock (&smdr->lock); 126 } 127 128 /* Make sure the shmem descriptor region is created */ 129 __CHECK_APP_SMDR; 130}; 131 132 133 134/** 135 * @INTERNAL 136 * Initialize __smdr pointer, if SMDR exits already. If not, create a new 137 * one. Once SMDR is created (as a bootmem named block), it is persistent. 138 */ 139static inline struct cvmx_shmem_smdr *__smdr_init() 140{ 141 const cvmx_bootmem_named_block_desc_t *smdr_nblk = NULL; 142 size_t smdr_size = sizeof(*__smdr); 143 char *smdr_name = CVMX_SHMEM_DSCPTR_NAME; 144 145 __smdr = (struct cvmx_shmem_smdr *) cvmx_bootmem_alloc_named(smdr_size, 0x10000, smdr_name); 146 147 if (__smdr) 148 __smdr_new (__smdr); 149 else 150 { 151 /* Check if SMDR exists already */ 152 smdr_nblk = cvmx_bootmem_find_named_block(smdr_name); 153 if (smdr_nblk) 154 { 155 __smdr = (struct cvmx_shmem_smdr *) 156 (cvmx_phys_to_ptr(smdr_nblk->base_addr)); 157 158 cvmx_spinlock_lock (&__smdr->lock); 159 if (smdr_nblk->size != smdr_size) 160 { 161 cvmx_dprintf("SMDR named block is created by another " 162 "application with different size %lu, " 163 "expecting %lu \n", 164 (long unsigned int)smdr_nblk->size, (long unsigned int)smdr_size); 165 __smdr = NULL; 166 } 167 cvmx_spinlock_unlock (&__smdr->lock); 168 } 169 } 170 171 if (!__smdr) 172 cvmx_dprintf("cvmx_shmem: Failed to allocate or find SMDR from bootmem \n"); 173 174 return __smdr; 175}; 176 177 178/** 179 * @INTERNAL 180 * Generic Iterator function for all SMDR entries 181 * 182 * @param void(*f)(dscptr) the function to be invoked for every descriptor 183 * @param param 184 * 185 * @return the descriptor iterator stopped at. 186 */ 187static struct cvmx_shmem_dscptr *__smdr_iterator(struct cvmx_shmem_dscptr *(*f)(struct cvmx_shmem_dscptr *dscptr, void *p), void *param ) 188{ 189 struct cvmx_shmem_dscptr *d, *dscptr = NULL; 190 int i; 191 192 __CHECK_APP_SMDR; 193 194 for (i = 0; i < CVMX_SHMEM_NUM_DSCPTR; i++) 195 { 196 d = &__smdr->shmd[i]; 197 if ((dscptr = (*f)(d, param)) != NULL) 198 break; /* stop iteration */ 199 } 200 201 return dscptr; 202} 203 204 205/** 206 * @INTERNAL 207 * SMDR name match functor. to be used for iterator. 208 * 209 * @param dscptr descriptor passed in by the iterator 210 * @param name string to match against 211 * 212 * @return !NULL descriptor matched 213 * NULL not match 214 */ 215static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_match_name(struct cvmx_shmem_dscptr *dscptr, void *name) 216{ 217 char *name_to_match = (char *) name; 218 struct cvmx_shmem_dscptr *ret = NULL; 219 220 if (dscptr->owner == CVMX_SHMEM_OWNER_NONE) 221 return NULL; 222 223 if (strcmp(dscptr->name, name_to_match) == 0) 224 ret = dscptr; 225 226 return ret; 227} 228 229/** 230 * @INTERNAL 231 * Find by name 232 * 233 * @param name string to match against 234 * 235 * @return !NULL descriptor matched 236 * NULL not match 237 */ 238static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_find_by_name(char *name) 239{ 240 return __smdr_iterator( __cvmx_shmem_smdr_match_name, name); 241} 242 243/** 244 * @INTERNAL 245 * SMDR is free functor. to be used for iterator. 246 * 247 * @param dscptr descriptor passed in by the iterator 248 * @param nouse 249 * 250 * @return !NULL descriptor is free 251 * NULL descriptor is not free 252 */ 253static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_is_free(struct cvmx_shmem_dscptr* dscptr, void *nouse) 254{ 255 if (dscptr->owner == CVMX_SHMEM_OWNER_NONE) 256 return dscptr; 257 else 258 return NULL; 259} 260 261/** 262 * @INTERNAL 263 * Search SMDR to find the first free descriptor 264 * 265 * @return !NULL free descriptor found 266 * NULL nothing found 267 */ 268struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_find_free_dscptr(void) 269{ 270 return __smdr_iterator(__cvmx_shmem_smdr_is_free, NULL); 271} 272 273/** 274 * @INTERNAL 275 * free a descriptor 276 * 277 * @param dscptr descriptor to be freed 278 */ 279static void __cvmx_shmem_smdr_free(struct cvmx_shmem_dscptr *dscptr) 280{ 281 dscptr->owner = CVMX_SHMEM_OWNER_NONE; 282} 283 284 285/** 286 * Per core shmem init function 287 * 288 * @return cvmx_shmem_smdr* pointer to __smdr 289 */ 290struct cvmx_shmem_smdr *cvmx_shmem_init() 291{ 292 return __smdr_init(); 293} 294 295/** 296 * Open shared memory based on named block 297 * 298 * @return dscptr descriptor of the opened named block 299 */ 300struct cvmx_shmem_dscptr *cvmx_shmem_named_block_open(char *name, uint32_t size, int oflag) 301{ 302 const cvmx_bootmem_named_block_desc_t *shmem_nblk = NULL; 303 struct cvmx_shmem_dscptr *dscptr = NULL; 304 int nblk_allocated = 0; /* Assume we don't need to allocate a new 305 bootmem block */ 306 void *vaddr = NULL; 307 const uint64_t size_4k = 4*1024, size_512mb = 512*1024*1024; 308 309 __CHECK_APP_SMDR; 310 311 /* Check size, Make sure it is minimal 4K, no bigger than 512MB */ 312 if (size > size_512mb) { 313 cvmx_dprintf("Shared memory size can not be bigger than 512MB \n"); 314 return NULL; 315 } 316 if (size < size_4k) 317 size = size_4k; 318 319 size = __upper_power_of_two(size); 320 321 cvmx_spinlock_lock(&__smdr->lock); 322 323 shmem_nblk = cvmx_bootmem_find_named_block(name); 324 if ((shmem_nblk == NULL) && (oflag & CVMX_SHMEM_O_CREAT)) 325 { 326 void *p; 327 /* The named block does not exist, create it if caller specifies 328 the O_CREAT flag */ 329 nblk_allocated = 1; 330 p = cvmx_bootmem_alloc_named(size, size, name); 331 if (p) 332 shmem_nblk = cvmx_bootmem_find_named_block(name); 333#ifdef DEBUG 334 cvmx_dprintf("cvmx-shmem-dbg:" 335 "creating a new block %s: blk %p, shmem_nblk %p \n", 336 name, p, shmem_nblk); 337#endif 338 } 339 340 if (shmem_nblk == NULL) 341 goto err; 342 343 /* We are now holding a valid named block */ 344 345 dscptr = __cvmx_shmem_smdr_find_by_name(name); 346 if (dscptr) 347 { 348 if (nblk_allocated) 349 { 350 /* name conflict between bootmem name space and SMDR name space */ 351 cvmx_dprintf("cvmx-shmem: SMDR descriptor name conflict, %s \n", name); 352 goto err; 353 } 354 /* Make sure size and alignment matches with existing descriptor */ 355 if ((size != dscptr->size) || (size != dscptr -> alignment)) 356 goto err; 357 } 358 else 359 { 360 /* Create a new descriptor */ 361 dscptr = __cvmx_shmem_smdr_find_free_dscptr(); 362 if (dscptr) 363 goto init; 364 else 365 { 366 cvmx_dprintf("cvmx-shmem: SMDR out of descriptors \n"); 367 goto err; 368 } 369 } 370 371 /* Maintain the reference count */ 372 if (dscptr != NULL) 373 dscptr->use_count += 1; 374 375 cvmx_spinlock_unlock(&__smdr->lock); 376 return dscptr; 377 378err: 379#ifdef DEBUG 380 cvmx_dprintf("cvmx-shmem-dbg: named block open failed \n"); 381#endif 382 383 if (dscptr) 384 __cvmx_shmem_smdr_free(dscptr); 385 if (shmem_nblk && nblk_allocated) 386 cvmx_bootmem_free_named(name); 387 cvmx_spinlock_unlock(&__smdr->lock); 388 389 return NULL; 390 391init: 392 393#ifdef DEBUG 394 cvmx_dprintf("cvmx-shmem-dbg: init SMDR descriptor %p \n", dscptr); 395#endif 396 397 /* Assign vaddr for single address space mapping */ 398 vaddr = __cvmx_shmem_vsbrk_64(size, size); 399 if (vaddr == NULL) { 400 /* Failed to allocate virtual address, clean up */ 401 goto err; 402 } 403 404#ifdef DEBUG 405 cvmx_dprintf("cmvx-shmem-dbg: allocated vaddr %p \n", vaddr); 406#endif 407 dscptr->vaddr = vaddr; 408 409 /* Store descriptor information, name, alignment,size... */ 410 dscptr->owner = cvmx_get_core_num(); 411 dscptr->is_named_block = 1; 412 dscptr->use_count = 1; 413 dscptr->name =shmem_nblk->name ; 414 dscptr->paddr = shmem_nblk->base_addr; 415 dscptr->size = size; 416 dscptr->alignment = size; 417 418 /* Store permission bits */ 419 if (oflag & CVMX_SHMEM_O_WRONLY) 420 dscptr->p_wronly = 1; 421 if (oflag & CVMX_SHMEM_O_RDWR) 422 dscptr->p_rdwr = 1; 423 424 cvmx_spinlock_unlock(&__smdr->lock); 425 return dscptr; 426} 427 428/** 429 * @INTERNAL 430 * 431 * For stand along SE application only. 432 * 433 * Add TLB mapping to map the shared memory 434 * 435 * @param dscptr shared memory descriptor 436 * @param pflag protection flags 437 * 438 * @return vaddr the virtual address mapped for the shared memory 439 */ 440#ifndef CVMX_BUILD_FOR_LINUX_USER 441void *__cvmx_shmem_map_standalone(struct cvmx_shmem_dscptr *dscptr, int pflag) 442{ 443 int free_index; 444 445 /* Find a free tlb entry */ 446 free_index = cvmx_tlb_allocate_runtime_entry(); 447 448 if (free_index < 0 ) 449 { 450 cvmx_dprintf("cvmx-shmem: shmem_map failed, out TLB entries \n"); 451 return NULL; 452 } 453 454#ifdef DEBUG 455 cvmx_dprintf("cmvx-shmem-dbg:" 456 "shmem_map TLB %d: vaddr %p paddr %lx, size %x \n", 457 free_index, dscptr->vaddr, dscptr->paddr, dscptr->size ); 458#endif 459 460 cvmx_tlb_write_runtime_entry(free_index, CAST64(dscptr->vaddr), 461 dscptr->paddr, dscptr->size, 462 TLB_DIRTY | TLB_VALID | TLB_GLOBAL); 463 464 return dscptr -> vaddr; 465} 466#endif 467 468/** 469 * @INTERNAL 470 * 471 * For Linux user application only 472 * 473 * Add mmap the shared memory 474 * 475 * @param dscptr shared memory descriptor 476 * @param pflag protection flags 477 * 478 * @return vaddr the virtual address mapped for the shared memory 479 */ 480#ifdef CVMX_BUILD_FOR_LINUX_USER 481static inline void *__cvmx_shmem_map_linux(struct cvmx_shmem_dscptr *dscptr, int pflag) 482{ 483 void *vaddr = NULL; 484 485 if(__cvmx_shmem_devmemfd == 0) 486 { 487 __cvmx_shmem_devmemfd = open("/dev/mem", O_RDWR); 488 if (__cvmx_shmem_devmemfd < 0) 489 { 490 cvmx_dprintf("Failed to open /dev/mem\n"); 491 exit(-1); 492 } 493 } 494 495 vaddr = mmap(dscptr->vaddr, dscptr->size, PROT_READ|PROT_WRITE, 496 MAP_SHARED, __cvmx_shmem_devmemfd, 0); 497 498 /* Make sure the mmap maps to the same virtual address specified in 499 * descriptor 500 */ 501 if ((vaddr!=NULL) && (vaddr != dscptr->vaddr)) 502 { 503 munmap(vaddr, dscptr->size); 504 vaddr = NULL; 505 } 506 return vaddr; 507} 508#endif 509 510/** 511 * cvmx_shmem API 512 * 513 * Add mapping for the shared memory 514 * 515 * @param dscptr shared memory descriptor 516 * @param pflag protection flags 517 * 518 * @return vaddr the virtual address mapped for the shared memory 519 */ 520void *cvmx_shmem_map(struct cvmx_shmem_dscptr *dscptr, int pflag) 521{ 522 void *vaddr = NULL; 523#ifdef CVMX_BUILD_FOR_LINUX_USER 524 vaddr = __cvmx_shmem_map_linux(dscptr, pflag); 525#else 526 vaddr = __cvmx_shmem_map_standalone(dscptr, pflag); 527#endif 528 return vaddr; 529} 530 531 532/** 533 * @INTERNAL 534 * 535 * For Linux user application only 536 * 537 * ummap the shared memory 538 * 539 * @param dscptr shared memory descriptor 540 * 541 */ 542#ifdef CVMX_BUILD_FOR_LINUX_USER 543static inline void __cvmx_shmem_unmap_linux(struct cvmx_shmem_dscptr* dscptr) 544{ 545 if (__cvmx_shmem_devmemfd && dscptr) 546 munmap(dscptr->vaddr, dscptr->size); 547} 548#endif 549 550 551/** 552 * @INTERNAL 553 * 554 * For stand along SE application only. 555 * 556 * ummap the shared memory 557 * 558 * @param dscptr shared memory descriptor 559 * 560 */ 561#ifndef CVMX_BUILD_FOR_LINUX_USER 562static inline void 563__cvmx_shmem_unmap_standalone(struct cvmx_shmem_dscptr *dscptr) 564{ 565 int index; 566 567 index = cvmx_tlb_lookup(CAST64(dscptr->vaddr)); 568 569#ifdef DEBUG 570 cvmx_dprintf("cmvx-shmem-dbg:" 571 "shmem_unmap TLB %d \n", index); 572#endif 573 cvmx_tlb_free_runtime_entry(index); 574} 575#endif 576 577/** 578 * ummap the shared memory 579 * 580 * @param dscptr shared memory descriptor 581 * 582 */ 583void cvmx_shmem_unmap(struct cvmx_shmem_dscptr *dscptr) 584{ 585#ifdef CVMX_BUILD_FOR_LINUX_USER 586 __cvmx_shmem_unmap_linux(dscptr); 587#else 588 __cvmx_shmem_unmap_standalone(dscptr); 589#endif 590} 591 592/** 593 * @INTERNAL 594 * 595 * Common implementation of closing a descriptor. 596 * 597 * @param dscptr shared memory descriptor 598 * @param remove 1: remove the descriptor and named block if this 599 * this is the last user of the descriptor 600 * 0: do not remove 601 * @return 0: Success 602 * !0: Failed 603 * 604 */ 605static inline int __cvmx_shmem_close_dscptr(struct cvmx_shmem_dscptr *dscptr, int remove) 606{ 607 cvmx_spinlock_lock(&dscptr->lock); 608 609 if (dscptr->use_count >0) 610 dscptr->use_count-= 1; 611 612 if ((dscptr->use_count == 0) && remove) 613 { 614 /* Free this descriptor */ 615 __cvmx_shmem_smdr_free(dscptr); 616 617 /* Free named block if this is the last user, and the block 618 is created by the application */ 619 if (dscptr->is_named_block) 620 { 621#ifdef DEBUG 622 cvmx_dprintf("cvmx-shmem-dbg: remove named block %s \n", dscptr->name); 623#endif 624 cvmx_bootmem_phy_named_block_free(dscptr->name, 0); 625 } 626 } 627 cvmx_spinlock_unlock(&dscptr->lock); 628 return 0; 629} 630 631 632/** 633 * @INTERNAL 634 * 635 * For stand along SE application only. 636 * 637 * close a descriptor. 638 * 639 * @param dscptr shared memory descriptor 640 * @param remove 1: remove the descriptor and named block if this 641 * this is the last user of the descriptor 642 * 0: do not remove 643 * @return 0: Success 644 * !0: Failed 645 * 646 */ 647#ifndef CVMX_BUILD_FOR_LINUX_USER 648static inline int __cvmx_shmem_close_standalone(struct cvmx_shmem_dscptr *dscptr, int remove) 649{ 650 return __cvmx_shmem_close_dscptr(dscptr, remove); 651} 652#endif 653 654/** 655 * @INTERNAL 656 * 657 * For Linux user application only. 658 * 659 * close a descriptor. 660 * 661 * @param dscptr shared memory descriptor 662 * @param remove 1: remove the descriptor and named block if this 663 * this is the last user of the descriptor 664 * 0: do not remove 665 * @return 0: Success 666 * !0: Failed 667 * 668 */ 669#ifdef CVMX_BUILD_FOR_LINUX_USER 670int __cvmx_shmem_close_linux(struct cvmx_shmem_dscptr *dscptr, int remove) 671{ 672 int ret; 673 ret = __cvmx_shmem_close_dscptr(dscptr, remove); 674 675 if (ret && __cvmx_shmem_devmemfd) 676 { 677 close(__cvmx_shmem_devmemfd); 678 __cvmx_shmem_devmemfd=0; 679 } 680 681 return ret; 682 683} 684#endif 685 686/** 687 * 688 * close a descriptor. 689 * 690 * @param dscptr shared memory descriptor 691 * @param remove 1: remove the descriptor and named block if this 692 * this is the last user of the descriptor 693 * 0: do not remove 694 * @return 0: Success 695 * !0: Failed 696 * 697 */ 698int cvmx_shmem_close(struct cvmx_shmem_dscptr *dscptr, int remove) 699{ 700 int ret; 701#ifdef CVMX_BUILD_FOR_LINUX_USER 702 ret = __cvmx_shmem_close_linux(dscptr, remove); 703#else 704 ret = __cvmx_shmem_close_standalone(dscptr, remove); 705#endif 706 return ret; 707} 708 709#ifdef DEBUG 710/** 711 * @INTERNAL 712 * SMDR non-free descriptor dump functor. to be used for iterator. 713 * 714 * @param dscptr descriptor passed in by the iterator 715 * 716 * @return NULL always 717 */ 718static struct cvmx_shmem_dscptr *__cvmx_shmem_smdr_display_dscptr(struct cvmx_shmem_dscptr *dscptr, void *nouse) 719{ 720 if ((dscptr != NULL ) && (dscptr -> owner != CVMX_SHMEM_OWNER_NONE)) 721 { 722 cvmx_dprintf(" %s: phy: %lx, size %d, alignment %lx, virt %p use_count %d\n", 723 dscptr->name, dscptr-> paddr, 724 dscptr->size, dscptr-> alignment, 725 dscptr->vaddr, dscptr->use_count); 726 } 727 728 return NULL; 729} 730#endif 731 732/** 733 * SMDR descriptor show 734 * 735 * list all non-free descriptors 736 */ 737void cvmx_shmem_show(void) 738{ 739 __CHECK_APP_SMDR; 740 741#ifdef DEBUG 742 cvmx_dprintf("SMDR descriptor list: \n"); 743 cvmx_spinlock_lock(&__smdr->lock); 744 __smdr_iterator(__cvmx_shmem_smdr_display_dscptr, NULL); 745 cvmx_spinlock_unlock(&__smdr->lock); 746 cvmx_dprintf("\n\n"); 747#endif 748} 749