unsigned int LBSM_Shmem_Update(HEAP heap, int/*bool*/ wait) { size_t heapsize = HEAP_Size(heap); void* heapbase = HEAP_Base(heap); unsigned int updated = 0; int i; assert(heapbase && heapsize); for (i = 0; i < 2; i++) { int can_wait = wait || (s_ShmemSize[i] && (!i || updated)); if (!s_Shmem_WLock(i, can_wait)) continue; /* Update shmem here: strict checks for the first time */ if (s_ShmemSize[i] != heapsize) { int shmid; void* shmem; s_Shmem_Destroy(i, s_ShmemSize[i] ? 0 : getpid()); if ((shmid = shmget(k_ShmemKey[i], heapsize, LBSM_SHM_PROT | IPC_CREAT | IPC_EXCL)) < 0 || !(shmem = shmat(shmid, 0, 0)) || shmem == (void*)(-1)) { CORE_LOGF_ERRNO_X(22, eLOG_Error, errno, ("Unable to re-create LBSM shmem[%d]", i + 1)); s_Shmem_WUnlock(i); return 0/*update failed*/; } s_Shmid[i] = shmid; s_Shmem[i] = shmem; s_ShmemSize[i] = heapsize; } memcpy(s_Shmem[i], heapbase, heapsize); if (!s_Shmem_WUnlock(i)) { CORE_LOGF_ERRNO_X(23, eLOG_Warning, errno, ("Update failed to unlock shmem[%d]", i + 1)); } updated |= 1 << i; } return updated; }
static void s_Shmem_Destroy(int which, pid_t own_pid) { if (s_Shmid[which] < 0) { assert(!s_Shmem[which] && !s_ShmemSize[which]); return; } if (s_Shmem[which]) { if (shmdt(s_Shmem[which]) < 0) { CORE_LOGF_ERRNO_X(14, eLOG_Error, errno, ("Unable to detach LBSM shmem[%d]", which + 1)); } s_Shmem[which] = 0; } if (own_pid) { struct shmid_ds shm_ds; if (shmctl(s_Shmid[which], IPC_STAT, &shm_ds) < 0) shm_ds.shm_cpid = 0; if (shm_ds.shm_cpid != own_pid) { if (shm_ds.shm_cpid) { CORE_LOGF_X(15, eLOG_Error, ("Not an owner (%lu) to remove LBSM shmem[%d]", (long) shm_ds.shm_cpid, which + 1)); } else { CORE_LOGF_ERRNO(eLOG_Error, errno, ("Unable to stat LBSM shmem[%d]", which + 1)); } own_pid = 0; } } else own_pid = 1; if (own_pid && shmctl(s_Shmid[which], IPC_RMID, 0) < 0) { CORE_LOGF_ERRNO_X(16, eLOG_Error, errno, ("Unable to remove LBSM shmem[%d]", which + 1)); } s_Shmid[which] = -1; s_ShmemSize[which] = 0; }
HEAP LBSM_Shmem_Attach(void) { int fallback = 0; /*FIXME: to become a parameter*/ int which; HEAP heap; #ifdef LBSM_DEBUG CORE_LOG(eLOG_Trace, "LBSM ATTACHING%s", fallback ? " (fallback)" : ""); #endif /*LBSM_DEBUG*/ if ((which = s_Shmem_RLock(!fallback)) < 0) { CORE_LOG_ERRNO_X(10, eLOG_Warning, errno, "Cannot lock LBSM shmem to attach"); return 0; } #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM R-lock[%d] acquired", which + 1)); #endif /*LBSM_DEBUG*/ if (!(heap = s_Shmem_Attach(which))) { int/*bool*/ attached = s_Shmem[which] != 0 ? 1/*true*/ : 0/*false*/; s_Shmem_RUnlock(which); CORE_LOGF_ERRNO_X(11, eLOG_Error, errno, ("Cannot %s LBSM shmem[%d]", attached ? "access" : "attach", which + 1)); } #ifdef LBSM_DEBUG else { CORE_LOGF(eLOG_Trace, ("LBSM heap[%p, %p, %d] attached", heap, HEAP_Base(heap), which + 1)); assert(HEAP_Serial(heap) == which + 1); } #endif /*LBSM_DEBUG*/ if (s_Shmem[which = !which]) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM shmem[%d] detached", which + 1)); #endif /*LBSM_DEBUG*/ shmdt(s_Shmem[which]); s_Shmem[which] = 0; s_Shmid[which] = -1; } else assert(s_Shmid[which] < 0); s_ShmemSize[which] = 0; return heap; }
/* Return non-zero if successful; return zero on failure (no lock acquired). * If "wait" passed greater than zero do not attempt to assassinate a contender * (try to identify and print its PID anyways). Otherwise, having killed * the contender, try to re-acquire the lock (without. */ static int/*bool*/ s_Shmem_WLock(int which, int/*bool*/ wait) { static union semun dummy; int locked; int pid; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM W-lock[%d] acquire%s", which + 1, wait ? " w/wait" : "")); #endif /*LBSM_DEBUG*/ if ((locked = s_Shmem_TryWLock(which)) == 0) return 1/*success*/; if (locked < 0) { /* even [1] was not successfully locked, so we have either * a/ a hanging writer, or * b/ a hanging old reader (which doesn't change [2] in block 0 only) * In either case, we can try to obtain a PID of the offender. */ if ((pid = semctl(s_Muxid, (which << 1) | 1, GETPID, dummy)) > 0) { int self = (pid_t) pid == getpid(); int other = !self && (kill(pid, 0) == 0 || errno == EPERM); int killed = 0; if (!wait) { if (other && kill(pid, SIGTERM) == 0) { CORE_LOGF_X(17, eLOG_Warning, ("Terminating PID %lu", (long) pid)); sleep(1); /* let them catch SIGTERM and exit gracefully */ kill(pid, SIGKILL); killed = 1; } else { CORE_LOGF_X(18, eLOG_Warning, ("Unable to kill PID %lu", (long) pid)); } } CORE_LOGF_X(19, eLOG_Warning, ("LBSM lock[%d] %s revoked from PID %lu (%s)", which + 1, killed ? "is being" : "has to be", (long) pid, self ? "self" : other ? (killed ? "killed" : "hanging") : "zombie")); } else if (pid < 0) return 0/*severe failure: most likely removed IPC id*/; } else { pid = 0; /* [1] was free (now locked) but [2] was taken by someone else, * we have a hanging reader, no additional info is available. */ if (!wait) { union semun arg; arg.val = 1; if (semctl(s_Muxid, (which << 1) + 2, SETVAL, arg) < 0) { CORE_LOGF_ERRNO_X(9, eLOG_Error, errno, ("Unable to adjust LBSM access count[%d]", which + 1)); } wait = 1/*this makes us undo [1] and fail*/; } if (wait) { int x_errno = errno; s_Shmem_Unlock(which, 1); errno = x_errno; } } if (!pid) { int val; if ((val = semctl(s_Muxid, (which << 1) + 2, GETVAL, dummy)) > 1) { CORE_LOGF_X(20, eLOG_Warning, ("%u hanging readers in LBSM shmem[%d]%s", (unsigned int) val, which + 1, wait ? "" : locked < 0 ? ", revoking lock" : ", lock revoked")); } else { CORE_LOGF_X(21, eLOG_Warning, ("A hanging reader in LBSM shmem[%d]%s", which + 1, wait ? "" : locked < 0 ? ", revoking lock" : ", lock revoked")); } } if (wait) return 0/*failure*/; if (locked > 0) return 1/*success: good to go*/; #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("LBSM W-lock[%d] re-acquire", which + 1)); #endif /*LBSM_DEBUG*/ return s_Shmem_WLock(which, 0/*no wait*/); }
static HEAP s_GetHeapCopy(TNCBI_Time now) { enum { eNone = 0, eAgain = 1, eFallback = 2 } retry = eNone; HEAP heap = 0; HEAP lbsm; for (;;) { const SLBSM_Version *c, *v; int serial = 0; CORE_LOCK_WRITE; if (s_Heap) { c = LBSM_GetVersion(s_Heap); assert(c && c->major == LBSM_HEAP_VERSION_MAJ); assert((void*) c == (void*) HEAP_Base(s_Heap)); if (c->entry.good < now) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] expired, dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } #ifdef LBSM_DEBUG else { CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] valid", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); } #endif /*LBSM_DEBUG*/ } else c = 0/*dummy for compiler not to complain*/; if (!(lbsm = LBSM_Shmem_Attach(retry == eFallback)) || (serial = HEAP_Serial(lbsm)) <= 0) { if (lbsm) { CORE_LOGF_X(1, eLOG_Error, ("Bad serial (%d) from LBSM heap attach", serial)); } /* else, an error has already been posted */ break; } if (!(v = LBSM_GetVersion(lbsm)) || (v->major < LBSM_HEAP_VERSION_MAJ || (v->major == LBSM_HEAP_VERSION_MAJ && v->minor < LBSM_HEAP_VERSION_MIN))) { if (v) { CORE_LOGF_X(2, eLOG_Error, ("LBSM heap[%d] version mismatch" " (current=%hu.%hu, expected=%u.%u+)", serial, v->major, v->minor, LBSM_HEAP_VERSION_MAJ, LBSM_HEAP_VERSION_MIN)); } else { CORE_LOGF_X(3, eLOG_Error, ("LBSM heap[%d] has no version", serial)); } break; } if (v->entry.good < now) { CORE_LOGF_X(4, eLOG_Warning, ("LBSM heap[%d] is out-of-date" " (current=%lu, expiry=%lu, delta=%lu)%s", serial, (unsigned long) now, (unsigned long) v->entry.good, (unsigned long) now -(unsigned long) v->entry.good, !retry && serial > 1 ? ", re-trying" : "")); if (!retry && serial > 1) { LBSM_Shmem_Detach(heap); retry = eFallback; CORE_UNLOCK; continue; } if (s_Heap) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } break; } assert((void*) v == (void*) HEAP_Base(lbsm)); if (s_Heap) { if (c->count == v->count && c->cksum == v->cksum) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] used", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ heap = s_Heap; break; } #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] is stale, dropped", s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap))); #endif /*LBSM_DEBUG*/ HEAP_Destroy(s_Heap); s_Heap = 0; } if (!(heap = HEAP_Copy(lbsm, 0, -serial))) { CORE_LOGF_ERRNO_X(6, eLOG_Error, errno, ("Unable to copy LBSM heap[%d]", serial)); break; } if (s_VerifyChecksum(heap, v->cksum)) { #ifdef LBSM_DEBUG CORE_LOGF(eLOG_Trace, ("Cached LBSM heap[%p, %p, %d] renewed", heap, HEAP_Base(heap), HEAP_Serial(heap))); #endif /*LBSM_DEBUG*/ s_Heap = heap; break; } CORE_LOGF_X(7, retry ? eLOG_Error : eLOG_Warning, ("LBSM heap[%p, %p, %d]%s checksum failure%s", (void*) heap, HEAP_Base(heap), HEAP_Serial(heap), retry == eAgain ? " persistent" : "", retry ? "" : ", re-trying")); verify(HEAP_Destroy(heap) == 0); heap = 0; if (retry) break; LBSM_Shmem_Detach(lbsm); retry = eAgain; CORE_UNLOCK; } assert(!heap || heap != lbsm); if (heap && heap == s_Heap) verify(HEAP_AddRef(s_Heap) > 1); LBSM_Shmem_Detach(lbsm); CORE_UNLOCK; return heap; }