1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
|
/****************************************************************
* *
* Copyright (c) 2014-2017 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include <sys/shm.h>
#include <sys/mman.h>
#include "gtm_ipc.h"
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "rtnobj.h"
#include "gtmio.h"
#include <rtnhdr.h>
#include "iosp.h"
#include "do_shmat.h"
#include "min_max.h"
#include "relqop.h"
#include "ipcrmid.h"
#include "interlock.h"
#include "gdsroot.h" /* for CDB_STAGNATE */
#include "incr_link.h" /* for NATIVE_HDR_LEN */
#include "hugetlbfs_overrides.h" /* for the ADJUST_SHM_SIZE_FOR_HUGEPAGES macro */
#include "gtm_permissions.h"
#include "cachectl.h"
#include "cacheflush.h"
/* Uncomment below line if you want RTNOBJ_DBG lines to be enabled */
/* #define RTNOBJ_DEBUG */
#ifdef RTNOBJ_DEBUG
# define RTNOBJ_DBG(X) X
#else
# define RTNOBJ_DBG(X)
#endif
#define DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX) \
{ \
assert(0 <= MIN_INDEX); \
assert(NUM_RTNOBJ_SHM_INDEX >= MIN_INDEX); \
assert(0 <= MAX_INDEX); \
assert(NUM_RTNOBJ_SHM_INDEX >= MAX_INDEX); \
}
/* Macro to make sure this process has attached to ALL rtnobj shared memory segments that currently exist for the
* relinkctl shared memory corresponding to LINKCTL. Need to execute this macro before checking if a given routine object
* does exist in one of the rtnobj shared memory segments.
*/
#define SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(LINKCTL, MIN_INDEX, MAX_INDEX, SHM_HDR, HAS_RELINKCTL_LOCK, RELINKREC) \
{ \
int pvt_max_index, i, pvt_shmid, shr_shmid; \
int save_errno; \
size_t shm_size; \
sm_uc_ptr_t shm_base; \
char errstr[256]; \
\
MAX_INDEX = SHM_HDR->rtnobj_max_shm_index; \
SHM_READ_MEMORY_BARRIER; /* Read "rtnobj_max_shm_index" BEFORE read memory barrier and everything else \
* else associated with this index AFTER the memory barrier. \
*/ \
pvt_max_index = LINKCTL->rtnobj_max_shm_index; \
DEBUG_ONLY( \
for (i = LINKCTL->rtnobj_min_shm_index; i < pvt_max_index; i++) \
{ \
pvt_shmid = LINKCTL->rtnobj_shmid[i]; \
shr_shmid = SHM_HDR->rtnobj_shmhdr[i].rtnobj_shmid; \
assert(pvt_shmid == shr_shmid); \
} \
) \
if (pvt_max_index != MAX_INDEX) \
{ \
MIN_INDEX = SHM_HDR->rtnobj_min_shm_index; \
DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX); /* assert validity of min_index/max_index */ \
for (i = pvt_max_index; i < MAX_INDEX; i++) \
{ \
pvt_shmid = LINKCTL->rtnobj_shmid[i]; \
shr_shmid = SHM_HDR->rtnobj_shmhdr[i].rtnobj_shmid; \
if (pvt_shmid != shr_shmid) \
{ \
assert(INVALID_SHMID == pvt_shmid); /* currently rtnobj shmids are never deleted */ \
shm_size = ((size_t)1 << (i + MIN_RTNOBJ_SHM_INDEX)); \
shm_base = (sm_uc_ptr_t)do_shmat_exec_perm(shr_shmid, shm_size, &save_errno); \
if (-1 == (sm_long_t)shm_base) \
{ \
if (HAS_RELINKCTL_LOCK) \
rel_latch(&SHM_HDR->relinkctl_latch); \
rel_latch(&RELINKREC->rtnobj_latch); \
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj2 shmat() failed for shmid=%d shmsize=0x%llx", \
shr_shmid, shm_size); \
if (!SHM_REMOVED(save_errno)) \
ISSUE_RELINKCTLERR_SYSCALL(&LINKCTL->zro_entry_name, errstr, save_errno); \
else \
ISSUE_REQRLNKCTLRNDWN_SYSCALL(LINKCTL, errstr, save_errno); \
} \
assert(0 == ((UINTPTR_T)shm_base % 8)); \
LINKCTL->rtnobj_shmid[i] = shr_shmid; \
LINKCTL->rtnobj_shm_base[i] = shm_base; \
} \
} \
LINKCTL->rtnobj_min_shm_index = MIN_INDEX; \
LINKCTL->rtnobj_max_shm_index = MAX_INDEX; \
} else \
{ \
MIN_INDEX = LINKCTL->rtnobj_min_shm_index; \
MAX_INDEX = pvt_max_index; \
DBG_ASSERT_INDEX_VALID(MIN_INDEX, MAX_INDEX); \
} \
}
#ifdef AUTORELINK_SUPPORTED
error_def(ERR_RELINKCTLERR);
error_def(ERR_RLNKRECLATCH);
error_def(ERR_RLNKSHMLATCH);
error_def(ERR_SYSCALL);
DEBUG_ONLY(GBLREF int saved_errno;)
DEBUG_ONLY(GBLREF int process_exiting;)
#ifdef DEBUG
/* Routine to verify the doubly-linked (fl/bl) freelists for each rtnobj size (various 2-powers) are in good shape */
void rtnobj_verify_freelist_fl_bl(rtnobjshm_hdr_t *rtnobj_shm_hdr, sm_uc_ptr_t shm_base)
{
int min_index, max_index, i;
que_ent_ptr_t freeList, elem, prev_elem;
rtnobj_hdr_t *rtnobj, *rtnobj2;
sm_off_t rtnobj_off;
gtm_uint64_t elemSize;
sm_uc_ptr_t shm_top;
freeList = &rtnobj_shm_hdr->freeList[0];
shm_top = shm_base + rtnobj_shm_hdr->shm_len;
for (i = 0; i < NUM_RTNOBJ_SIZE_BITS; i++, freeList++)
{
if (NULL_RTNOBJ_SM_OFF_T != freeList->fl)
{
assert(NULL_RTNOBJ_SM_OFF_T != freeList->bl);
assert(0 == freeList->fl % 8);
prev_elem = NULL;
elem = (que_ent_ptr_t)(shm_base + freeList->fl);
do
{
rtnobj = (rtnobj_hdr_t *)((sm_uc_ptr_t)elem - OFFSETOF(rtnobj_hdr_t, userStorage));
assert(STATE_FREE == rtnobj->state);
assert(i == rtnobj->queueIndex);
rtnobj_off = (sm_uc_ptr_t)rtnobj - shm_base;
elemSize = ((gtm_uint64_t)1 << (rtnobj->queueIndex + MIN_RTNOBJ_SIZE_BITS));
rtnobj2 = (rtnobj_hdr_t *)(shm_base + (rtnobj_off ^ elemSize)); /* address of buddy */
assert(((sm_uc_ptr_t)rtnobj2 == shm_top) || (rtnobj2->queueIndex <= rtnobj->queueIndex));
if (NULL_RTNOBJ_SM_OFF_T == elem->bl)
assert(NULL == prev_elem);
else
assert(((NULL == prev_elem) && (NULL_RTNOBJ_SM_OFF_T == elem->bl))
|| (prev_elem->fl == -elem->bl));
if (NULL_RTNOBJ_SM_OFF_T == elem->fl)
{
assert(elem == (que_ent_ptr_t)(shm_base + freeList->bl));
break;
}
assert(0 == elem->fl % 8);
prev_elem = elem;
elem = (que_ent_ptr_t)((sm_uc_ptr_t)elem + elem->fl);
}
while (TRUE);
} else
assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl);
}
}
/* Routine to verify the correctness of derived fields, rtnobj_shm_hdr->rtnobj_min_free_index & rtnobj->rtnobj_max_free_index */
void rtnobj_verify_min_max_free_index(rtnobjshm_hdr_t *rtnobj_shm_hdr)
{
int min_index, max_index, i;
que_ent_ptr_t freeList;
min_index = NUM_RTNOBJ_SIZE_BITS;
max_index = 0;
freeList = &rtnobj_shm_hdr->freeList[0];
for (i = 0; i < NUM_RTNOBJ_SIZE_BITS; i++, freeList++)
{
if (NULL_RTNOBJ_SM_OFF_T != freeList->fl)
{
if (min_index > i)
min_index = i;
if (max_index < (i + 1))
max_index = i + 1;
}
}
assert(min_index == rtnobj_shm_hdr->rtnobj_min_free_index);
assert(max_index == rtnobj_shm_hdr->rtnobj_max_free_index);
}
#endif
/* Insert "new_tail" at tail of the doubly-linked relative-queue starting from "que_base".
* Note "que_base" is a pointer to relinkctl shared memory whereas "new_tail" is a pointer to rtnobj shared memory.
* Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm.
* "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets.
*/
void insqt_rtnobj(que_ent_ptr_t new_tail, que_ent_ptr_t que_base, sm_uc_ptr_t shm_base)
{
que_ent_ptr_t old_tail;
RTNOBJ_DBG(fprintf(stderr, "insqt_rtnobj : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \
(UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)new_tail);)
if (NULL_RTNOBJ_SM_OFF_T == que_base->bl)
{ /* Queue has nothing */
assert(NULL_RTNOBJ_SM_OFF_T == que_base->fl);
que_base->fl = (uchar_ptr_t)new_tail - (uchar_ptr_t)shm_base;
assert(0 == que_base->fl % 8);
que_base->bl = que_base->fl;
new_tail->fl = NULL_RTNOBJ_SM_OFF_T;
new_tail->bl = NULL_RTNOBJ_SM_OFF_T;
} else
{
old_tail = (que_ent_ptr_t)(shm_base + que_base->bl);
assert(NULL_RTNOBJ_SM_OFF_T == old_tail->fl);
new_tail->fl = NULL_RTNOBJ_SM_OFF_T;
new_tail->bl = (uchar_ptr_t)old_tail - (uchar_ptr_t)new_tail;
assert(0 == new_tail->bl % 8);
old_tail->fl = -new_tail->bl;
que_base->bl += old_tail->fl;
}
return;
}
/* Remove an element from the head of the doubly-linked relative-queue starting from "que_base" and return a pointer to it.
* Note "que_base" is a pointer to relinkctl shared memory whereas the returned value is a pointer to rtnobj shared memory.
* Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm.
* "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets.
*/
rtnobj_hdr_t *remqh_rtnobj(que_ent_ptr_t que_base, sm_uc_ptr_t shm_base)
{
que_ent_ptr_t ret, new_head;
rtnobj_hdr_t *rtnobj;
assert(NULL_RTNOBJ_SM_OFF_T != que_base->fl);
ret = (que_ent_ptr_t)(shm_base + que_base->fl);
RTNOBJ_DBG(fprintf(stderr, "remqh_rtnobj : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \
(UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)ret);)
assert(0 == (sm_off_t)ret % 8);
assert(NULL_RTNOBJ_SM_OFF_T == ret->bl);
if (NULL_RTNOBJ_SM_OFF_T == ret->fl)
{ /* Empty queue after this removal */
que_base->fl = NULL_RTNOBJ_SM_OFF_T;
que_base->bl = NULL_RTNOBJ_SM_OFF_T;
} else
{
new_head = (que_ent_ptr_t)((uchar_ptr_t)ret + ret->fl);
assert(0 == (sm_off_t)new_head % 8);
new_head->bl = NULL_RTNOBJ_SM_OFF_T;
assert(0 == que_base->fl % 8);
que_base->fl += ret->fl;
}
rtnobj = (rtnobj_hdr_t *)((sm_uc_ptr_t)ret - OFFSETOF(rtnobj_hdr_t, userStorage));
return rtnobj;
}
/* Remove a specific element "rtnobj" from wherever it is in the doubly-linked relative-queue starting from "que_base".
* Note "que_base" is a pointer to relinkctl shared memory whereas "rtnobj" is a pointer to rtnobj shared memory.
* Hence the need for NULL_RTNOBJ_SM_OFF_T to indicate a link from rtnobj shm to relinkctl shm.
* "shm_base" is pointer to the start of rtnobj shared memory. Used to calculate relative offsets.
*/
void remq_rtnobj_specific(que_ent_ptr_t que_base, sm_uc_ptr_t shm_base, rtnobj_hdr_t *rtnobj)
{
que_ent_ptr_t elem, new_head, rtnque;
assert(NULL_RTNOBJ_SM_OFF_T != que_base->fl);
rtnque = (que_ent_ptr_t)((sm_uc_ptr_t)rtnobj + OFFSETOF(rtnobj_hdr_t, userStorage));
RTNOBJ_DBG(fprintf(stderr, "remqh_rtnobj_specific : que_base = 0x%llx : shm_base = 0x%llx : elem = 0x%llx\n", \
(UINTPTR_T)que_base, (UINTPTR_T)shm_base, (UINTPTR_T)rtnque);)
# ifdef DEBUG
/* Verify that "rtnobj" is actually in the queue first */
elem = (que_ent_ptr_t)(shm_base + que_base->fl);
assert(NULL_RTNOBJ_SM_OFF_T == elem->bl);
while (elem != rtnque)
{
assert(NULL_RTNOBJ_SM_OFF_T != elem->fl);
elem = (que_ent_ptr_t)((sm_uc_ptr_t)elem + elem->fl);
}
# endif
if (NULL_RTNOBJ_SM_OFF_T == rtnque->bl)
{ /* rtnobj is at head of queue */
if (NULL_RTNOBJ_SM_OFF_T == rtnque->fl)
{ /* rtnobj is only element in queue */
que_base->fl = NULL_RTNOBJ_SM_OFF_T;
que_base->bl = NULL_RTNOBJ_SM_OFF_T;
} else
{
elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->fl);
assert(0 == (sm_off_t)elem % 8);
elem->bl = NULL_RTNOBJ_SM_OFF_T;
assert(0 == que_base->fl % 8);
assert(0 == rtnque->fl % 8);
que_base->fl += rtnque->fl;
}
} else if (NULL_RTNOBJ_SM_OFF_T == rtnque->fl)
{ /* rtnobj is at tail of queue with at least one more element in queue */
elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->bl);
elem->fl = NULL_RTNOBJ_SM_OFF_T;
assert(0 == que_base->bl % 8);
assert(0 == rtnque->bl % 8);
que_base->bl += rtnque->bl;
} else
{ /* rtnobj is in middle of queue with at least one element in queue on either side */
elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->bl);
assert(0 == (sm_off_t)elem % 8);
assert(0 == elem->fl % 8);
assert(0 == rtnque->fl % 8);
elem->fl += rtnque->fl;
assert(0 == (sm_off_t)rtnque % 8);
assert(0 == rtnque->fl % 8);
elem = (que_ent_ptr_t)((uchar_ptr_t)rtnque + rtnque->fl);
assert(0 == elem->bl % 8);
assert(0 == rtnque->bl % 8);
elem->bl += rtnque->bl;
}
}
/* Function to allocate "objSize" bytes of space, for the object file that "fd" points to and has a hash value "objhash",
* in rtnobj shared memory for the relinkctl file/shared-memory derived from "zhist". If such an object already exists, return
* a pointer to that location in rtnobj shared memory after incrementing reference-counts (to indicate how many processes
* have linked in to this shared object). In this fast-path case, we hold a lock only on the relink record corresponding
* to this object's routine-name. In the other case (where space has to be allocated), we also hold a lock on the entire
* relinkctl shared memory (and effectively all rtnobj shared memory segments).
*/
sm_uc_ptr_t rtnobj_shm_malloc(zro_hist *zhist, int fd, off_t objSize, gtm_uint64_t objhash)
{
boolean_t has_relinkctl_lock, has_rtnobj_lock;
boolean_t initialized, return_NULL, need_shmctl;
char errstr[256];
gtm_uint64_t src_cksum_8byte;
int min_index, max_index, min_free_index, max_free_index, shm_index;
int objIndex, tSize, sizeIndex, shmSizeIndex, shmid, actualSize;
int codelen, loopcnt, save_errno;
open_relinkctl_sgm *linkctl;
que_ent_ptr_t freeList;
relinkrec_t *relinkrec;
relinkshm_hdr_t *shm_hdr;
sm_uc_ptr_t shm_base;
rtnobj_hdr_t *rtnobj, *prev_rtnobj, *rtnobj2;
rtnobj_sm_off_t shm_index_off;
rtnobjshm_hdr_t *rtnobj_shm_hdr;
size_t shm_size;
off_t rtnobjSize;
sm_off_t shm_off;
sm_uc_ptr_t objBuff, codeadr;
rhdtyp tmprhd, *rhdr;
gtm_uint64_t elemSize;
zro_validation_entry *zhent;
struct stat dir_stat_buf;
int stat_res;
int user_id;
int group_id;
int perm;
int maxvers, curvers;
struct shmid_ds shmstat;
struct perm_diag_data pdd;
# ifdef DEBUG
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
# endif
zhent = zhist->end - 1;
relinkrec = zhent->relinkrec;
linkctl = zhent->relinkctl_bkptr;
/* Assert certain design assumptions */
assert((MAX_RTNOBJ_SHM_INDEX + RTNOBJ_SHMID_INDEX_MAXBITS) <= (8 * SIZEOF(rtnobj_sm_off_t)));
assert(((size_t)1 << RTNOBJ_SHMID_INDEX_MAXBITS) >= MAX_RTNOBJ_SHM_INDEX);
shm_hdr = GET_RELINK_SHM_HDR(linkctl);
loopcnt = 0;
has_relinkctl_lock = FALSE;
assert(!linkctl->locked);
assert(!linkctl->hdr->file_deleted);
if (!grab_latch(&relinkrec->rtnobj_latch, RLNKREC_LATCH_TIMEOUT_SEC))
{
assert(FALSE);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5)
ERR_RLNKRECLATCH, 3, relinkrec->rtnname_fixed.c, RTS_ERROR_MSTR(&linkctl->zro_entry_name));
}
do
{
if (CDB_STAGNATE <= loopcnt++)
{ /* We have tried THRICE to search without a lock but found something inconsistent (due to concurrent
* changes to shared memory structures). Get a lock on the entire relinkctl file before searching
* for one last time.
*/
assert(!has_relinkctl_lock);
if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC))
{
assert(FALSE);
rel_latch(&relinkrec->rtnobj_latch);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4)
ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name));
}
has_relinkctl_lock = TRUE;
}
/* Check if we are in sync with the rtnobj shared memory segments corresponding to this relinkctl shared memory.
* If not attach to those rtnobj shmids first.
*/
SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(linkctl, min_index, max_index, shm_hdr, has_relinkctl_lock, relinkrec);
/* Now that we have attached to the necessary routine buffer shmids, search within shared memory for the
* routine buffer whose <objhash,objLen> matches ours.
*/
assert(MAXUINT4 >= objSize);
shm_index_off = relinkrec->rtnobj_shm_offset;
prev_rtnobj = NULL;
maxvers = relinkrec->numvers;
curvers = 0;
while ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off)
{
assertpro(maxvers >= curvers++); /* assertpro to avoid infinite loops in PRO (just in case) */
shm_index = RTNOBJ_GET_SHM_INDEX(shm_index_off);
shm_off = RTNOBJ_GET_SHM_OFFSET(shm_index_off);
assert(0 == (shm_off % 8));
/* Since we hold the rtnobj_latch we should see valid values of shm_index/shm_off */
assert(min_index <= shm_index);
assert(max_index > shm_index);
assert(INVALID_SHMID != linkctl->rtnobj_shmid[shm_index]);
assert(shm_off < ((off_t)1 << (MIN_RTNOBJ_SHM_INDEX + shm_index)));
rtnobj = (rtnobj_hdr_t *)(linkctl->rtnobj_shm_base[shm_index] + shm_off);
assert((rtnobj->objhash != objhash) || (rtnobj->objLen == objSize));
if ((rtnobj->objLen == objSize) && (rtnobj->objhash == objhash))
break;
prev_rtnobj = rtnobj;
shm_index_off = rtnobj->next_rtnobj_shm_offset;
}
if ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off)
{ /* If found return */
assert(rtnobj->initialized);
/* If gtm_autorelink_keeprtn is FALSE (i.e. we maintain reference counts even on process exit),
* we do not expect refcnt to go to high values as it is the # of processes concurrently running
* and actively using this buffer and that cannot be close to 2**31 which is the max value for refcnt.
*/
assert(TREF(gtm_autorelink_keeprtn) || (REFCNT_INACCURATE != rtnobj->refcnt));
if (REFCNT_INACCURATE != rtnobj->refcnt)
rtnobj->refcnt++; /* increment refcnt while holding the rtnobj_lock */
if (has_relinkctl_lock)
rel_latch(&shm_hdr->relinkctl_latch);
RTNOBJ_DBG(fprintf(stderr, "rtnobj_shm_malloc : rtnname = %s : objhash = 0x%llx : " \
"linkctl = 0x%llx : Found : refcnt = %d : elem = 0x%llx\n", \
relinkrec->rtnname_fixed.c, (UINTPTR_T)objhash, \
(UINTPTR_T)linkctl, rtnobj->refcnt, \
(UINTPTR_T)&rtnobj->userStorage.userStart);)
zhent->cycle = relinkrec->cycle; /* Update private cycle to be in sync with shared copy */
rel_latch(&relinkrec->rtnobj_latch);
objBuff = (sm_uc_ptr_t)&rtnobj->userStorage.userStart;
return objBuff; /* object code starts after rtnobj_hdr_t */
}
assert(MAXUINT4 >= (objSize + OFFSETOF(rtnobj_hdr_t, userStorage)));
assert(4 <= SIZEOF(tSize)); /* above assert guarantees 4-bytes is enough for storage */
tSize = objSize + OFFSETOF(rtnobj_hdr_t, userStorage);
sizeIndex = ceil_log2_32bit(tSize);
assert(MAX_RTNOBJ_SIZE_BITS >= sizeIndex);
sizeIndex = MAX(sizeIndex, MIN_RTNOBJ_SIZE_BITS); /* Round up to minimum supported obj file size */
/* Find the smallest shm index that can possibly contain space for sizeIndex */
shm_index = (MIN_RTNOBJ_SHM_INDEX > sizeIndex) ? 0 : sizeIndex - MIN_RTNOBJ_SHM_INDEX;
sizeIndex -= MIN_RTNOBJ_SIZE_BITS;
shm_index = MAX(shm_index, shm_hdr->min_shm_index);
/* If not found, allocate space in shared memory for one and return. Now that we are going to
* play with the buddy list structures in shared memory, get an overarching lock on all rtnobj shmids
* i.e. a lock on the entire relinkctl file
*/
if (!has_relinkctl_lock)
{
if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC))
{
assert(FALSE);
rel_latch(&relinkrec->rtnobj_latch);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4)
ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name));
}
has_relinkctl_lock = TRUE;
}
/* Sync up with rtnobj shmids one final time if needed now that we have the relinkctl file lock */
SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED(linkctl, min_index, max_index, shm_hdr, has_relinkctl_lock, relinkrec);
/* Find a shared memory buffer to hold the routine object */
rtnobj = NULL;
for ( ; shm_index < max_index; shm_index++)
{
rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index];
if (INVALID_SHMID == rtnobj_shm_hdr->rtnobj_shmid)
continue;
min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index;
max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index;
shm_base = linkctl->rtnobj_shm_base[shm_index];
objIndex = MAX(sizeIndex, min_free_index);
freeList = &rtnobj_shm_hdr->freeList[objIndex];
for ( ; objIndex < max_free_index; objIndex++, freeList++)
{
assert(objIndex < NUM_RTNOBJ_SIZE_BITS);
if (NULL_RTNOBJ_SM_OFF_T == freeList->fl)
{
assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl);
continue;
}
rtnobj = remqh_rtnobj(freeList, shm_base);
if (NULL_RTNOBJ_SM_OFF_T == freeList->fl)
{
assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl);
/* This was the only element in the freeList.
* Set min_free_index/max_free_index to impossible values
* if the new minimums/maximums need to be recomputed.
*/
if (objIndex == min_free_index)
min_free_index = NUM_RTNOBJ_SIZE_BITS;
if (max_free_index == (objIndex + 1))
max_free_index = 0;
} else if (min_free_index > sizeIndex)
{
assert(NUM_RTNOBJ_SIZE_BITS > min_free_index);
min_free_index = NUM_RTNOBJ_SIZE_BITS;
}
break;
}
if (NULL != rtnobj)
break;
}
if (NULL == rtnobj)
{ /* Need to allocate rtnobj buffer in a new shared memory segment */
assert(shm_index >= max_index);
shm_size = ((size_t)1 << (shm_index + MIN_RTNOBJ_SHM_INDEX));
ADJUST_SHM_SIZE_FOR_HUGEPAGES(shm_size, shm_size); /* second parameter "shm_size" is adjusted size */
/* If minimum huge page size is much higher than requested shm size, adjust "shm_index"
* accordingly so we use the allocated (bigger-than-requested) shm completely.
*/
shmSizeIndex = ceil_log2_64bit(shm_size);
assert(((size_t)1 << shmSizeIndex) == shm_size);
assert(shmSizeIndex >= (shm_index + MIN_RTNOBJ_SHM_INDEX));
shm_index = shmSizeIndex - MIN_RTNOBJ_SHM_INDEX;
shmid = shmget(IPC_PRIVATE, shm_size, RWDALL | IPC_CREAT);
if (-1 == shmid)
{
save_errno = errno;
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmget() failed for shmsize=0x%llx", shm_size);
ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno);
}
STAT_FILE(linkctl->zro_entry_name.addr, &dir_stat_buf, stat_res);
if (-1 == stat_res)
{
save_errno = errno;
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
shm_rmid(shmid); /* if error removing shmid we created, just move on */
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj stat() of file %s failed", linkctl->zro_entry_name.addr);
ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno);
}
if (!gtm_permissions(&dir_stat_buf, &user_id, &group_id, &perm, PERM_IPC|PERM_EXEC, &pdd))
{
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
shm_rmid(shmid); /* if error removing shmid we created, just move on */
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(10 + PERMGENDIAG_ARG_COUNT)
ERR_RELINKCTLERR, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name),
ERR_PERMGENFAIL, 4, RTS_ERROR_STRING("rtnobj"),
RTS_ERROR_MSTR(&linkctl->zro_entry_name),
PERMGENDIAG_ARGS(pdd));
}
if (-1 == shmctl(shmid, IPC_STAT, &shmstat))
{
save_errno = errno;
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
shm_rmid(shmid); /* if error removing shmid we created, just move on */
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmctl(IPC_STAT) failed for shmid=%d shmsize=0x%llx",
shmid, shm_size);
ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno);
}
/* change uid, group-id and permissions if needed */
need_shmctl = FALSE;
if ((INVALID_UID != user_id) && (user_id != shmstat.shm_perm.uid))
{
shmstat.shm_perm.uid = user_id;
need_shmctl = TRUE;
}
if ((INVALID_GID != group_id) && (group_id != shmstat.shm_perm.gid))
{
shmstat.shm_perm.gid = group_id;
need_shmctl = TRUE;
}
if (shmstat.shm_perm.mode != perm)
{
shmstat.shm_perm.mode = perm;
need_shmctl = TRUE;
}
if (need_shmctl && (-1 == shmctl(shmid, IPC_SET, &shmstat)))
{
save_errno = errno;
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
shm_rmid(shmid); /* if error removing shmid we created, just move on */
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmctl(IPC_SET) failed for shmid=%d shmsize=0x%llx",
shmid, shm_size);
ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno);
}
shm_base = (sm_uc_ptr_t)do_shmat_exec_perm(shmid, shm_size, &save_errno);
if (-1 == (sm_long_t)shm_base)
{
save_errno = errno;
rel_latch(&shm_hdr->relinkctl_latch);
rel_latch(&relinkrec->rtnobj_latch);
shm_rmid(shmid); /* if error removing shmid we created, just move on */
SNPRINTF(errstr, SIZEOF(errstr), "rtnobj shmat() failed for shmid=%d shmsize=0x%llx",
shmid, shm_size);
ISSUE_RELINKCTLERR_SYSCALL(&linkctl->zro_entry_name, errstr, save_errno);
}
assert(shm_index < NUM_RTNOBJ_SHM_INDEX);
rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index];
objIndex = (shm_index + MIN_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SIZE_BITS);
assert(objIndex < NUM_RTNOBJ_SIZE_BITS);
freeList = &rtnobj_shm_hdr->freeList[objIndex];
rtnobj = (rtnobj_hdr_t *)shm_base;
assert(NULL_RTNOBJ_SM_OFF_T == freeList->fl);
assert(NULL_RTNOBJ_SM_OFF_T == freeList->bl);
assert(sizeIndex <= objIndex);
if (sizeIndex < objIndex)
{ /* rtnobj_shm_hdr->rtnobj_min_free_index and ->rtnobj_min_free_index will be set a little later */
min_free_index = NUM_RTNOBJ_SIZE_BITS;
max_free_index = 0;
} else
{ /* rtnobj_shm_hdr->rtnobj_min_free_index and ->rtnobj_min_free_index are already set correctly.
* set min_free_index/max_free_index such that the above two dont get set again (a little later).
*/
assert(NUM_RTNOBJ_SIZE_BITS == rtnobj_shm_hdr->rtnobj_min_free_index);
assert(0 == rtnobj_shm_hdr->rtnobj_max_free_index);
assert(NUM_RTNOBJ_SIZE_BITS != objIndex);
min_free_index = objIndex;
assert(0 != NUM_RTNOBJ_SIZE_BITS);
max_free_index = NUM_RTNOBJ_SIZE_BITS;
}
rtnobj_shm_hdr->rtnobj_shmid = shmid;
assert(0 == rtnobj_shm_hdr->real_len);
rtnobj_shm_hdr->shm_len = shm_size;
if (shm_hdr->rtnobj_min_shm_index > shm_index)
{
assert(NUM_RTNOBJ_SHM_INDEX == shm_hdr->rtnobj_min_shm_index);
shm_hdr->rtnobj_min_shm_index = shm_index;
}
assert(linkctl->rtnobj_max_shm_index == shm_hdr->rtnobj_max_shm_index);
assert(shm_hdr->rtnobj_max_shm_index < (shm_index + 1));
SHM_WRITE_MEMORY_BARRIER; /* Update everything except "rtnobj_max_shm_index" BEFORE write memory barrier */
shm_hdr->rtnobj_max_shm_index = (shm_index + 1);
/* Sync shared memory with private contents for this process to avoid duplicate shmget/shmat */
linkctl->rtnobj_min_shm_index = shm_hdr->rtnobj_min_shm_index;
linkctl->rtnobj_max_shm_index = shm_hdr->rtnobj_max_shm_index;
linkctl->rtnobj_shm_base[shm_index] = shm_base;
linkctl->rtnobj_shmid[shm_index] = shmid;
}
/* At this point, we have a non-null rtnobj but it could be bigger than we want.
* Split it into half until we reach the desired size. Insert into free queues along the way.
*/
assert(objIndex >= sizeIndex);
if (objIndex > sizeIndex)
{
if (0 == max_free_index)
{
rtnobj_shm_hdr->rtnobj_max_free_index = objIndex;
max_free_index = objIndex;
}
rtnobjSize = ((off_t)1 << (objIndex + MIN_RTNOBJ_SIZE_BITS));
freeList = &rtnobj_shm_hdr->freeList[objIndex];
do
{
rtnobjSize /= 2;
objIndex--;
freeList--;
rtnobj2 = (rtnobj_hdr_t *)((sm_uc_ptr_t)rtnobj + rtnobjSize);
rtnobj2->state = STATE_FREE;
rtnobj2->queueIndex = objIndex;
DEBUG_ONLY(shm_off = (sm_uc_ptr_t)rtnobj2 - shm_base);
assert(0 == (shm_off % ((sm_off_t)1 << (objIndex + MIN_RTNOBJ_SIZE_BITS))));
/* The following fields need initialization only when state becomes STATE_ALLOCATED.
* rtnobj2->initialized
* rtnobj2->refcnt
* rtnobj2->src_cksum_8byte
* rtnobj2->next_rtnobj_shm_offset
* rtnobj2->relinkctl_index
* rtnobj2->objLen
*/
insqt_rtnobj(&rtnobj2->userStorage.freePtr, freeList, shm_base);
} while (objIndex > sizeIndex);
if (NUM_RTNOBJ_SIZE_BITS == min_free_index)
rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex;
} else
{
if (NUM_RTNOBJ_SIZE_BITS == min_free_index)
{
assert(objIndex == rtnobj_shm_hdr->rtnobj_min_free_index);
if (0 == max_free_index)
{ /* Need to recompute BOTH rtnobj_shm_hdr->rtnobj_min_free_index
* AND rtnobj_shm_hdr->rtnobj_max_free_index. But the fact that
* min and max are the same implies there are NO more free buffers
* in this entire shared memory so the recomputation is easy.
*/
assert((objIndex + 1) == rtnobj_shm_hdr->rtnobj_max_free_index);
rtnobj_shm_hdr->rtnobj_max_free_index = 0;
rtnobj_shm_hdr->rtnobj_min_free_index = NUM_RTNOBJ_SIZE_BITS;
} else
{ /* Need to ONLY recompute rtnobj_shm_hdr->rtnobj_min_free_index */
max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index;
freeList = &rtnobj_shm_hdr->freeList[objIndex];
for ( ; objIndex < max_free_index; objIndex++, freeList++)
{
assert(objIndex < NUM_RTNOBJ_SIZE_BITS);
if (NULL_RTNOBJ_SM_OFF_T != freeList->fl)
{
rtnobj_shm_hdr->rtnobj_min_free_index = objIndex;
break;
}
}
assert(objIndex < max_free_index);
}
} else if (0 == max_free_index)
{ /* Need to ONLY recompute rtnobj_shm_hdr->rtnobj_min_free_index */
assert((objIndex + 1) == rtnobj_shm_hdr->rtnobj_max_free_index);
min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index;
freeList = &rtnobj_shm_hdr->freeList[objIndex];
for ( ; objIndex >= min_free_index; objIndex--, freeList--)
{
assert(objIndex < NUM_RTNOBJ_SIZE_BITS);
if (NULL_RTNOBJ_SM_OFF_T != freeList->fl)
{
rtnobj_shm_hdr->rtnobj_max_free_index = objIndex + 1;
break;
}
}
assert(objIndex >= min_free_index);
}
}
rtnobj->state = STATE_ALLOCATED;
rtnobj->queueIndex = sizeIndex;
rtnobj_shm_hdr->real_len += objSize;
elemSize = ((gtm_uint64_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS));
rtnobj_shm_hdr->used_len += elemSize;
DEBUG_ONLY(rtnobj_verify_min_max_free_index(rtnobj_shm_hdr));
DEBUG_ONLY(rtnobj_verify_freelist_fl_bl(rtnobj_shm_hdr, shm_base);)
/* Now that we have gotten a "rtnobj" pointer, we can release the relinkctl latch on shared memory.
* We still need to hold on to the lower-granular relinkrec->rtnobj_latch to read the object file
* into shared memory.
*/
rel_latch(&shm_hdr->relinkctl_latch);
assert(((off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS)) >= (objSize + OFFSETOF(rtnobj_hdr_t, userStorage)));
assert(((off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS - 1)) < (objSize + OFFSETOF(rtnobj_hdr_t, userStorage)));
shm_off = (sm_uc_ptr_t)rtnobj - shm_base;
assert(0 == (shm_off % ((sm_off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS))));
rtnobj->initialized = TRUE;
rtnobj->refcnt = 1;
assert(0 <= shm_off);
assert(shm_off < rtnobj_shm_hdr->shm_len);
shm_index_off = RTNOBJ_SET_SHM_INDEX_OFF(shm_index, shm_off);
if (NULL != prev_rtnobj)
prev_rtnobj->next_rtnobj_shm_offset = shm_index_off;
else
relinkrec->rtnobj_shm_offset = shm_index_off;
rtnobj->next_rtnobj_shm_offset = (rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T;
rtnobj->relinkctl_index = relinkrec - linkctl->rec_base;
rtnobj->objLen = objSize;
rtnobj->objhash = objhash;
relinkrec->objLen += objSize;
relinkrec->usedLen += elemSize;
relinkrec->numvers++;
/* Fill the shared memory buffer with the object code */
objBuff = (sm_uc_ptr_t)&rtnobj->userStorage.userStart;
if (-1 == lseek(fd, NATIVE_HDR_LEN, SEEK_SET))
return_NULL = TRUE;
else
{
DOREADRL(fd, objBuff, objSize, actualSize);
return_NULL = (actualSize != objSize);
}
if (return_NULL)
{
/* Free allocated object in shared memory using rtnobj_shm_free. Set up a dummy rtnhdr
* with just the necessary fields initialized since rtnobj_shm_free requires it.
*/
rhdr = &tmprhd;
rhdr->shared_ptext_adr = objBuff + SIZEOF(rhdtyp);
rhdr->zhist = zhist;
DEBUG_ONLY(rhdr->shared_object = TRUE); /* for an assert inside rtnobj_shm_free */
DEBUG_ONLY(rhdr->relinkctl_bkptr = linkctl); /* for an assert inside rtnobj_shm_free */
/* We already hold the relinkrec->rtnobj_latch. Free up the shared object before releasing this
* lock to avoid other processes from seeing the allocated shared object and assuming it is
* properly initialized when actually it is not. rtnobj_shm_free obtains the relinkrec->rtnobj_latch
* which would fail an assert since we already hold that latch so add a dbg-flag to avoid an assert.
*/
rtnobj_shm_free(rhdr, LATCH_GRABBED_TRUE); /* Note: this will release relinkrec->rtnobj_latch for us */
return NULL; /* caller will issue error */
}
rhdr = (rhdtyp *)objBuff;
codelen = rhdr->ptext_end_adr - rhdr->ptext_adr; /* Length of object code */
assert(0 < codelen);
codeadr = objBuff + (UINTPTR_T)rhdr->ptext_adr; /* Pointer to object code */
cacheflush(codeadr, codelen, BCACHE); /* Cacheflush executable part from instruction cache */
RTNOBJ_DBG(fprintf(stderr, "rtnobj_shm_malloc : rtnname = %s : objhash = 0x%llx : " \
"linkctl = 0x%llx : Inserted : refcnt = %d : elem = 0x%llx : numvers = %d\n", \
relinkrec->rtnname_fixed.c, (UINTPTR_T)objhash, \
(UINTPTR_T)linkctl, rtnobj->refcnt, \
(UINTPTR_T)objBuff, relinkrec->numvers);)
relinkrec->objhash = objhash;
++relinkrec->cycle; /* cycle bump is because of the implicit ZRUPDATE that linking a routine does */
if (0 == relinkrec->cycle)
relinkrec->cycle = 1; /* cycle value of 0 has special meaning, so bump to 1 in case we roll over */
zhent->cycle = relinkrec->cycle; /* Update private cycle to be in sync with shared copy */
rel_latch(&relinkrec->rtnobj_latch);
return objBuff;
} while (TRUE);
}
/* Function to free the object file in rtnobj shared memory whose routine header is "rhead". If this object file is used
* by more than one process (i.e. reference count is > 1), then decrement the reference count and return. This fast path
* only holds lock on the relink record corresponding to this object's routine-name. If, however, the reference count is 1,
* space for this object file will be freed up and that requires holding a lock on the entire relinkctl shared memory (and
* effectively all rtnobj shared memory segments).
*/
void rtnobj_shm_free(rhdtyp *rhead, boolean_t latch_grabbed)
{
relinkrec_t *relinkrec;
open_relinkctl_sgm *linkctl;
relinkshm_hdr_t *shm_hdr;
int maxObjIndex, sizeIndex;
int min_index, max_index, min_free_index, max_free_index, shm_index;
gtm_uint64_t elemSize, origElemSize, objLen;
rtnobj_hdr_t *rtnobj, *prev_rtnobj, *rtnobj2;
rtnobj_sm_off_t shm_index_off;
rtnobjshm_hdr_t *rtnobj_shm_hdr;
sm_uc_ptr_t shm_base;
sm_off_t shm_off, rtnobj_off;
que_ent_ptr_t freeList;
zro_validation_entry *zhent;
sm_uc_ptr_t objBuff;
int maxvers, curvers;
DEBUG_ONLY(size_t shm_size;)
DEBUG_ONLY(int dbg_shm_index;)
# ifdef DEBUG
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
# endif
/* Note that if breakpoints are in effect, rhead->shared_ptext_adr will not be equal to rhead->ptext_adr.
* This is because we would have taken a private copy of the code (for breakpoints) into ptext_adr and kept
* shared_ptext_adr untouched. In that case, it is still possible this function is called as part of process
* exit (when breakpoints on a shared routine object are still active). We will still need to free-up/decrement-refcnt
* the shared copy (shared_ptext_adr). So look at shared_ptext_adr below instead of ptext_adr;
*/
assert(rhead->shared_object);
assert(NULL != rhead->shared_ptext_adr);
if (NULL == rhead->shared_ptext_adr)
return; /* in pro, be safe */
assert(process_exiting || (rhead->shared_ptext_adr == rhead->ptext_adr));
objBuff = rhead->shared_ptext_adr - SIZEOF(rhdtyp);
rtnobj = (rtnobj_hdr_t *)(objBuff - OFFSETOF(rtnobj_hdr_t, userStorage));
assert(STATE_ALLOCATED == rtnobj->state);
assert(rtnobj->initialized);
assert(0 < rtnobj->refcnt);
assert(NULL != rhead->zhist);
zhent = rhead->zhist->end - 1;
relinkrec = zhent->relinkrec;
linkctl = zhent->relinkctl_bkptr;
assert(rhead->relinkctl_bkptr == linkctl);
shm_hdr = GET_RELINK_SHM_HDR(linkctl);
assert(!linkctl->locked);
assert(!linkctl->hdr->file_deleted);
sizeIndex = rtnobj->queueIndex;
# ifdef DEBUG
/* Find shm_index corresponding to this routine buffer to cross check later */
min_index = linkctl->rtnobj_min_shm_index;
max_index = linkctl->rtnobj_max_shm_index;
for (dbg_shm_index = min_index; dbg_shm_index < max_index; dbg_shm_index++)
{
rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[dbg_shm_index];
if (INVALID_SHMID == rtnobj_shm_hdr->rtnobj_shmid)
continue;
shm_base = linkctl->rtnobj_shm_base[dbg_shm_index];
assert(NULL != shm_base);
shm_size = ((size_t)1 << (dbg_shm_index + MIN_RTNOBJ_SHM_INDEX));
if (((gtm_uint64_t)shm_base <= (gtm_uint64_t)rtnobj)
&& (((gtm_uint64_t)shm_base + shm_size) > (gtm_uint64_t)rtnobj))
break; /* shared memory corresponding to rtnobj is found */
}
assert(dbg_shm_index < max_index);
# endif
if (!latch_grabbed && !grab_latch(&relinkrec->rtnobj_latch, RLNKREC_LATCH_TIMEOUT_SEC))
{
assert(FALSE);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5)
ERR_RLNKRECLATCH, 3, relinkrec->rtnname_fixed.c, RTS_ERROR_MSTR(&linkctl->zro_entry_name));
}
assert(0 < rtnobj->refcnt);
assert(TREF(gtm_autorelink_keeprtn) || (REFCNT_INACCURATE != rtnobj->refcnt));
if (REFCNT_INACCURATE != rtnobj->refcnt)
rtnobj->refcnt--; /* decrement refcnt while holding the rtnobj_lock */
if (rtnobj->refcnt)
{ /* The loaded object cannot be freed until refcnt becomes 0. But caller's job done. Return. */
RTNOBJ_DBG(fprintf(stderr, \
"rtnobj_shm_free : rtnname = %s : objhash = 0x%llx : Decremented : refcnt = %d : elem = 0x%llx\n", \
relinkrec->rtnname_fixed.c, (UINTPTR_T)rtnobj->objhash, rtnobj->refcnt, \
(UINTPTR_T)objBuff);)
rel_latch(&relinkrec->rtnobj_latch);
return;
}
assert(sizeIndex == rtnobj->queueIndex); /* since this pid has not freed it yet, queueIndex must not change concurrently */
/* Note: Although we hold the rtnobj_latch, it is possible that new rtnobj_shmids get created concurrently
* and an addition happens to the rtnobj->next_rtnobj_shm_offset linked list pointing to the new shmids.
* But since additions to the linked list happen only at the end, we are guaranteed that the element of
* interest (rtnobj which we want to free and have already located in rtnobj_shm_malloc) would be found
* without reaching the tail portion of that linked list (which would require attaching to the new shmid).
* So no need to invoke the SYNC_RTNOBJ_SHMID_CYCLE_IF_NEEDED macro like we needed to in "rtnobj_shm_malloc".
*/
shm_index_off = relinkrec->rtnobj_shm_offset;
prev_rtnobj = NULL;
maxvers = relinkrec->numvers;
curvers = 0;
while ((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off)
{
assertpro(maxvers >= curvers++); /* assertpro to avoid infinite loops in PRO (just in case) */
shm_index = RTNOBJ_GET_SHM_INDEX(shm_index_off);
shm_off = RTNOBJ_GET_SHM_OFFSET(shm_index_off);
assert(0 == (shm_off % 8));
/* Since we hold the rtnobj_latch we should see valid values of shm_index/shm_off */
assert(min_index <= shm_index);
assert(max_index > shm_index);
assert(INVALID_SHMID != linkctl->rtnobj_shmid[shm_index]);
assert(shm_off < ((off_t)1 << (MIN_RTNOBJ_SHM_INDEX + shm_index)));
rtnobj2 = (rtnobj_hdr_t *)(linkctl->rtnobj_shm_base[shm_index] + shm_off);
if (rtnobj2 == rtnobj)
break;
prev_rtnobj = rtnobj2;
shm_index_off = rtnobj2->next_rtnobj_shm_offset;
}
assertpro((rtnobj_sm_off_t)NULL_RTNOBJ_SM_OFF_T != shm_index_off);
shm_index_off = rtnobj->next_rtnobj_shm_offset;
assert(dbg_shm_index == shm_index);
rtnobj_shm_hdr = &shm_hdr->rtnobj_shmhdr[shm_index];
shm_base = linkctl->rtnobj_shm_base[shm_index];
maxObjIndex = (shm_index + MIN_RTNOBJ_SHM_INDEX - MIN_RTNOBJ_SIZE_BITS);
assert(sizeIndex <= maxObjIndex);
elemSize = origElemSize = ((gtm_uint64_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS));
/* Now that we need to unload the object from shared memory, get a shared memory lock */
if (!grab_latch(&shm_hdr->relinkctl_latch, RLNKSHM_LATCH_TIMEOUT_SEC))
{
assert(FALSE);
rel_latch(&relinkrec->rtnobj_latch);
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_RLNKSHMLATCH, 2, RTS_ERROR_MSTR(&linkctl->zro_entry_name));
}
assert(rtnobj->objLen <= rtnobj_shm_hdr->real_len);
assert(elemSize <= rtnobj_shm_hdr->used_len);
objLen = rtnobj->objLen;
rtnobj_shm_hdr->real_len -= objLen;
rtnobj_shm_hdr->used_len -= elemSize;
/* Check if buddy of "rtnobj" is free and if so free the two together. Merge the two into a bigger chunk
* and redo the free-buddy-check/merge as much as possible.
*/
min_free_index = rtnobj_shm_hdr->rtnobj_min_free_index;
if (sizeIndex < min_free_index)
{
min_free_index = sizeIndex;
rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex; /* we are about to do a insqt_rtnobj at freeList[sizeIndex] */
}
freeList = &rtnobj_shm_hdr->freeList[sizeIndex];
rtnobj_off = (sm_uc_ptr_t)rtnobj - shm_base;
while (sizeIndex < maxObjIndex)
{
rtnobj2 = (rtnobj_hdr_t *)(shm_base + (rtnobj_off ^ elemSize)); /* address of buddy */
assert(rtnobj2->queueIndex <= sizeIndex);
assert((STATE_ALLOCATED == rtnobj2->state) || (STATE_FREE == rtnobj2->state));
if (STATE_ALLOCATED == rtnobj2->state)
break;
if (rtnobj2->queueIndex != sizeIndex)
break;
remq_rtnobj_specific(freeList, shm_base, rtnobj2);
if ((NULL_RTNOBJ_SM_OFF_T == freeList->fl) && (sizeIndex == min_free_index))
min_free_index = NUM_RTNOBJ_SIZE_BITS;
if (rtnobj2 < rtnobj)
{
assert(elemSize == ((sm_off_t)rtnobj - (sm_off_t)rtnobj2));
rtnobj = rtnobj2; /* Pick lower address buddy for top of new bigger block */
assert(rtnobj_off >= (sm_off_t)elemSize);
rtnobj_off -= elemSize;
}
freeList++;
sizeIndex++;
elemSize *= 2;
}
rtnobj->state = STATE_FREE;
rtnobj->queueIndex = sizeIndex;
assert(rtnobj_off == ((sm_uc_ptr_t)rtnobj - shm_base));
assert(0 == (rtnobj_off % ((sm_off_t)1 << (sizeIndex + MIN_RTNOBJ_SIZE_BITS))));
insqt_rtnobj(&rtnobj->userStorage.freePtr, freeList, shm_base);
max_free_index = rtnobj_shm_hdr->rtnobj_max_free_index;
if (sizeIndex >= max_free_index)
rtnobj_shm_hdr->rtnobj_max_free_index = sizeIndex + 1; /* Recompute max_free_index */
if (NUM_RTNOBJ_SIZE_BITS == min_free_index)
{ /* Recompute min_free_index */
max_free_index = sizeIndex + 1;
sizeIndex = rtnobj_shm_hdr->rtnobj_min_free_index;
freeList = &rtnobj_shm_hdr->freeList[sizeIndex];
for ( ; sizeIndex < max_free_index; sizeIndex++, freeList++)
{
assert(sizeIndex < NUM_RTNOBJ_SIZE_BITS);
if (NULL_RTNOBJ_SM_OFF_T != freeList->fl)
break;
}
rtnobj_shm_hdr->rtnobj_min_free_index = sizeIndex;
}
DEBUG_ONLY(rtnobj_verify_min_max_free_index(rtnobj_shm_hdr));
DEBUG_ONLY(rtnobj_verify_freelist_fl_bl(rtnobj_shm_hdr, shm_base);)
rel_latch(&shm_hdr->relinkctl_latch);
if (NULL != prev_rtnobj)
prev_rtnobj->next_rtnobj_shm_offset = shm_index_off;
else
relinkrec->rtnobj_shm_offset = shm_index_off;
assert(objLen <= relinkrec->objLen);
relinkrec->objLen -= objLen;
assert(origElemSize <= relinkrec->usedLen);
relinkrec->usedLen -= origElemSize;
assert(0 < relinkrec->numvers);
relinkrec->numvers--;
RTNOBJ_DBG(fprintf(stderr, \
"rtnobj_shm_free : rtnname = %s : objhash = 0x%llx : Freed : refcnt = %d : elem = 0x%llx : numvers = %d\n", \
relinkrec->rtnname_fixed.c, (UINTPTR_T)rtnobj->objhash, rtnobj->refcnt, \
(UINTPTR_T)objBuff, relinkrec->numvers);)
rel_latch(&relinkrec->rtnobj_latch);
return;
}
#endif
|